Проектная работа по анализу рынка заведений общественного питания Москвы¶

ОГЛАВЛЕНИЕ:

  1. Описание проекта

  2. Обзор данных - загрузка данных и ознакомление с общей информацией.

  3. Предобработка данных - необходимо провести необходимую предобработку:

    • дубликаты;
    • обработка пропусков;
    • создание дополнительных столбцов
    • предобработка выбросов
  4. Исследовательский анализ данных:

    • Категории заведений.
    • Количества посадочных мест.
    • Сетевых и несетевых заведений.
    • Анализ топ-15 сетевых заведений.
    • Количество заведений по административным районам.
    • Рейтинг заведений.
    • Карта заведений по Москве
    • Топ-15 улиц по количеству заведений
    • Улицы с 1 заведением
    • Ценовой диапазон.
    • Круглосуточные заведения.
    • Заведения с низкими рейтингами.
    • Вывод
  5. Детлизированное исследование: открытие кофейни

    • Количество и расположение кофейн
    • Время работы кофейн
    • Рейтинги кофейн и их распределение по районам
    • Цены
    • Топ-10 сетевых кофейн
    • Проверка наличия корреляции
    • Топ-15 улиц
    • Вывод
  6. Подвести итоги и написать рекомендации

ПРЕЗЕНТАЦИЯ: https://disk.yandex.ru/d/d-u1ec5Dap5lWQ

Описание проекта:¶

Инвесторы из фонда «Shut Up and Take My Money» решили попробовать себя в новой области и открыть заведение общественного питания в Москве. Заказчики ещё не знают, что это будет за место: кафе, ресторан, пиццерия, паб или бар, — и какими будут расположение, меню и цены. Необходимо провести анализ рынка заведений общественного питания Москвы, для дальнейшего принятия решения в выборе подходящего инвесторам места.

Цель исследования:

  1. Провести исследование рынка заведений общественного питания Москвы и найти интересные особенности.

  2. Провести детализированное исследование: открытие кофейни.

Для решения поставленной цели определим этапы исследования.

Ход исследования:

  1. Загрузка данных и ознакомление с общей информацией.

  2. Предобработка данных - необходимо провести необходимую предобработку (дубликаты, пропуски, дополнительные столбцы).

  3. Исследовательский анализ данных. Провести анализ показателей:

    • Категории заведений.
    • Количества посадочных мест.
    • Сетевых и несетевых заведений.
    • Названия заведений.
    • Количество заведений по административным районам.
    • Рейтинг заведений.
    • Карта заведений по Москве
    • Топ-15 улиц по количеству заведений
    • Улицы с 1 заведением
    • Ценовой диапазон.
    • Круглосуточные заведения.
    • Заведения с низкими рейтингами.
  4. Детлизированное исследование: открытие кофейни. Исследовать:

    • Количество и расположение кофейн
    • Время работы кофейн
    • Рейтинги кофейн и их распределение по районам
    • Цены
  5. Подвести итоги и написать рекомендации

Для проведения исследования доступен файл с заведениями общественного питания Москвы, составленный на основе данных сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года, который содержит следующую информацию:

  • название заведения;
  • адрес заведения;
  • категория заведения, например «кафе», «пиццерия» или «кофейня»;
  • информация о днях и часах работы;
  • широта географической точки, в которой находится заведение;
  • долгота географической точки, в которой находится заведение;
  • рейтинг заведения по оценкам пользователей в Яндекс Картах (высшая оценка — 5.0);
  • категория цен в заведении, например «средние», «ниже среднего», «выше среднего» и так далее;
  • строка, которая хранит среднюю стоимость заказа в виде диапазона
  • число с оценкой среднего чека, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Средний счёт»
  • число с оценкой одной чашки капучино, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Цена одной чашки капучино»
  • число, выраженное 0 или 1, которое показывает, является ли заведение сетевым (для маленьких сетей могут встречаться ошибки)
  • административный район, в котором находится заведение, например Центральный административный округ;
  • количество посадочных мест.

Информация, размещённая в сервисе Яндекс Бизнес, могла быть добавлена пользователями или найдена в общедоступных источниках. Она носит исключительно справочный характер.

Шаг 1. Загрузка данных и обзор информации¶

Загруим необходимые библиотеки и код игнорирования сообщений предупреждений

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import folium
import json
from plotly import graph_objects as go
from folium import Map, Choropleth
from folium import Map, Marker 
from folium.plugins import MarkerCluster
from folium.features import CustomIcon
import re

import warnings; warnings.filterwarnings(action='ignore')
/Users/mariapolivanova/anaconda3/envs/practicum/lib/python3.9/site-packages/scipy/__init__.py:146: UserWarning: A NumPy version >=1.17.3 and <1.25.0 is required for this version of SciPy (detected version 1.26.4
  warnings.warn(f"A NumPy version >={np_minversion} and <{np_maxversion}"

Откроем файл с данными

In [2]:
#считываем информацию и сохраняем в датафреймы
try:
    df = pd.read_csv(' .csv')  
except FileNotFoundError:
    df = pd.read_csv(' .csv')

Датафрейм содержит большое количество данных, для вывода всех столбцов снимим ограничение на вывод

In [3]:
# снимаем ограничение на количество столбцов
pd.set_option('display.max_columns', None)

# снимаем ограничение на ширину столбцов
pd.set_option('display.max_colwidth', None)

Для ознокомления с данными посмотрим общую информацию, первые строки датафрейма, на распределение значений в данных, наличие пропущенных значений и явных дубликатов.

In [4]:
#Зададим функцию для просмотра общей информации, первых строк датафрейма, 
#на распределение значений в данных, наличие пропущенных значений и явных дубликатов
def general_info(dataframe):   
    return display(
        dataframe.info(),
        '*'*50,
        dataframe.describe(),
        '*'*50,
        dataframe.head(5),
        '*'*50,
        'Пропущенные значения среди данных:', dataframe.isna().sum(),
        '*'*50,
        'Дубликатов среди данных:', dataframe.duplicated().sum()
    )
In [5]:
#посмотрим в разрезе заданной функции на данные moscow_places.csv
general_info(df)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8406 entries, 0 to 8405
Data columns (total 14 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               8406 non-null   object 
 1   category           8406 non-null   object 
 2   address            8406 non-null   object 
 3   district           8406 non-null   object 
 4   hours              7870 non-null   object 
 5   lat                8406 non-null   float64
 6   lng                8406 non-null   float64
 7   rating             8406 non-null   float64
 8   price              3315 non-null   object 
 9   avg_bill           3816 non-null   object 
 10  middle_avg_bill    3149 non-null   float64
 11  middle_coffee_cup  535 non-null    float64
 12  chain              8406 non-null   int64  
 13  seats              4795 non-null   float64
dtypes: float64(6), int64(1), object(7)
memory usage: 919.5+ KB
None
'**************************************************'
lat lng rating middle_avg_bill middle_coffee_cup chain seats
count 8406.000000 8406.000000 8406.000000 3149.000000 535.000000 8406.000000 4795.000000
mean 55.750109 37.608570 4.229895 958.053668 174.721495 0.381275 108.421689
std 0.069658 0.098597 0.470348 1009.732845 88.951103 0.485729 122.833396
min 55.573942 37.355651 1.000000 0.000000 60.000000 0.000000 0.000000
25% 55.705155 37.538583 4.100000 375.000000 124.500000 0.000000 40.000000
50% 55.753425 37.605246 4.300000 750.000000 169.000000 0.000000 75.000000
75% 55.795041 37.664792 4.400000 1250.000000 225.000000 1.000000 140.000000
max 55.928943 37.874466 5.000000 35000.000000 1568.000000 1.000000 1288.000000
'**************************************************'
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats
0 WoWфли кафе Москва, улица Дыбенко, 7/1 Северный административный округ ежедневно, 10:00–22:00 55.878494 37.478860 5.0 NaN NaN NaN NaN 0 NaN
1 Четыре комнаты ресторан Москва, улица Дыбенко, 36, корп. 1 Северный административный округ ежедневно, 10:00–22:00 55.875801 37.484479 4.5 выше среднего Средний счёт:1500–1600 ₽ 1550.0 NaN 0 4.0
2 Хазри кафе Москва, Клязьминская улица, 15 Северный административный округ пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00 55.889146 37.525901 4.6 средние Средний счёт:от 1000 ₽ 1000.0 NaN 0 45.0
3 Dormouse Coffee Shop кофейня Москва, улица Маршала Федоренко, 12 Северный административный округ ежедневно, 09:00–22:00 55.881608 37.488860 5.0 NaN Цена чашки капучино:155–185 ₽ NaN 170.0 0 NaN
4 Иль Марко пиццерия Москва, Правобережная улица, 1Б Северный административный округ ежедневно, 10:00–22:00 55.881166 37.449357 5.0 средние Средний счёт:400–600 ₽ 500.0 NaN 1 148.0
'**************************************************'
'Пропущенные значения среди данных:'
name                    0
category                0
address                 0
district                0
hours                 536
lat                     0
lng                     0
rating                  0
price                5091
avg_bill             4590
middle_avg_bill      5257
middle_coffee_cup    7871
chain                   0
seats                3611
dtype: int64
'**************************************************'
'Дубликатов среди данных:'
0

В полученном датафрейме:

  • 14 столбцов, наименования приведены к змеиному регистру
  • 8 406 заведений;
  • явных дубликатов не выявлено, стоит проверить на неявные дубликаты;
  • типы данных: float64, int64, object;
  • рейтин по 5-ти бальной шкале, аномалий не выявлено - минимальное значение - 1, максимальное - 5;
  • посадочных мест от 0 до 1288;
  • пропущенные значения в данных:
    • hours - 536
    • price - 5091
    • avg_bill - 4590
    • middle_avg_bill - 5257
    • middle_coffee_cup - 7871
    • seats - 3611
  • проверить временной промежуток за который предоставлены данные нет возможности.

Вернуться в начало проекта

Шаг 2. Предобработка данных¶

Проверка наличия и обработка дубликатов¶

In [6]:
# Для сравнения сколько данных останется после предобработки 
#выведем сумму заведений до предобработки
before_processing = df['name'].count()
print(
    'Количество заведений в датафрейме до предобработки:', 
    before_processing
)
Количество заведений в датафрейме до предобработки: 8406
In [7]:
# посмотрим на уникальные значения по разным столбцам, 
# где могут быть неявные дубликаты.
print('Уникальные названия категорий зааведений:')
print(df['category'].unique())
print('_'*80)
print('Уникальные значения времени работы:')
print(df['hours'].unique())
print('_'*80)
print('Уникальные категории сетевых/несетевых заведений:')
print(df['chain'].unique())
print('_'*80)
print('Уникальные наименования административных районов Москвы:')
print(df['district'].unique())
Уникальные названия категорий зааведений:
['кафе' 'ресторан' 'кофейня' 'пиццерия' 'бар,паб' 'быстрое питание'
 'булочная' 'столовая']
________________________________________________________________________________
Уникальные значения времени работы:
['ежедневно, 10:00–22:00'
 'пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00'
 'ежедневно, 09:00–22:00' ... 'пн-пт 08:30–21:30; сб,вс 09:00–21:30'
 'пн-чт 13:00–22:00; пт,сб 13:00–22:30; вс 13:00–22:00'
 'пн-сб 10:30–21:30']
________________________________________________________________________________
Уникальные категории сетевых/несетевых заведений:
[0 1]
________________________________________________________________________________
Уникальные наименования административных районов Москвы:
['Северный административный округ'
 'Северо-Восточный административный округ'
 'Северо-Западный административный округ'
 'Западный административный округ' 'Центральный административный округ'
 'Восточный административный округ' 'Юго-Восточный административный округ'
 'Южный административный округ' 'Юго-Западный административный округ']

Среди категорий заведений, сетевых/несетевых категорий, и наименований административных районов дубликатов не обнаружено.

Среди значений времени работы заведений слишком разнообразные значения, их трудно исследовать на дубликаты, но это не так и важно.

Посмотрим наличие дубликатов среди наименований заведений.

In [8]:
print(
    'Количество уникальных названий заведений:', 
    df.name.nunique()
)
Количество уникальных названий заведений: 5614
In [9]:
#Посмотрим наличие дубликатов среди наименований заведений.
print(
    'Всего дубликатов среди наименований заведений', 
    df['name'].duplicated().sum()
)
Всего дубликатов среди наименований заведений 2792
In [10]:
#sorted(df['name'].unique())

Нормальное явление что среди наименований есть дубликаты, так как среди заведений есть сетевые, которые имеют одинаковое название. Приведем все наименования заведений и адреса к нижнему регистру, и проверим на дубликаты среди названий и адресов одновременно.

In [11]:
#Приведем все наименования заведений и адреса к нижнему регистру
df['name'] = df['name'].str.lower()
df['address'] = df['address'].str.lower()
In [12]:
print(
    'Дубликатов по наименованию и географическому местоположению:',
    df.duplicated(
        subset=['name', 'address', 'lat', 'lng', 'category']
    ).sum()
)
Дубликатов по наименованию и географическому местоположению: 1

Выявлено 1 неявный дубликат заведения с одинаковыми наименованими, адресами, категорей, широтой и долготой географической точки. Такие дубликаты стоит удалить.

In [13]:
#посмотрим на строки с неявными дубликатами
df[df[['name', 'address', 'lat', 'lng', 'category']].duplicated(
    keep=False
)]
Out[13]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats
1430 more poke ресторан москва, волоколамское шоссе, 11, стр. 2 Северный административный округ ежедневно, 09:00–21:00 55.806307 37.497566 4.2 NaN NaN NaN NaN 0 188.0
1511 more poke ресторан москва, волоколамское шоссе, 11, стр. 2 Северный административный округ пн-чт 09:00–18:00; пт,сб 09:00–21:00; вс 09:00–18:00 55.806307 37.497566 4.2 NaN NaN NaN NaN 1 188.0
In [14]:
#удалим найденный дубликат
df = df.drop_duplicates(
    subset = ['name', 'address', 'lat', 'lng', 'category'], 
    keep = 'first'
).reset_index(drop=True)
In [15]:
#посмотрим на строки с неявными дубликатами в случае разности в наименовании
#df[df[['address', 'lat', 'lng', 'category']].duplicated(keep=False)]

Выявлены заведения с одинаковым адресом, географическим положением и категорией и схожие по названию но разные по написанию:

  • "кафе-кулинария сикварули" / "сикварули";
  • "dragon bubble tea" / "dragon mixology bar";
  • "леон" / "leon".

Это дубликаты, их нужно удалить.

In [16]:
# приведем наименования дубликатов к единому названию и удалим их      
df['name'] =df['name'].str.replace('кафе-кулинария сикварули', 'сикварули')
df['name'] =df['name'].str.replace('dragon bubble tea', 'dragon mixology bar')
df['name'] =df['name'].str.replace('леон', 'leon')

#удалим найденный дубликат
df = df.drop_duplicates(
    subset = ['name', 'address', 'lat', 'lng', 'category'], 
    keep = 'first'
).reset_index(drop=True)
In [17]:
# Уберем из названий заведений верхний литерал "'"
df['name'] = df['name'].str.replace("'", '')

# Посмотрим сколько осталось уникальных наименований заведений после предобработки
print('Количество уникальных занчений "name":', df['name'].unique().shape[0])
Количество уникальных занчений "name": 5508

До предобработки было 5614 уникикальных наименования, после предобработки стало 5508. Дубликаты были удалены. Перейдем к изучению пропусков.

Обработка пропусков¶

Как было выявлено на этапе ознакомления с датафреймом встречаются строки с пропущенными значениями по некоторым данным.

  • hours (время работы заведения) - 536;
  • price (категория цен) - 5091;
  • avg_bill (средняя стоимость заказа) - 4590;
  • middle_avg_bill (число с оценкой среднего чека) - 5257;
  • middle_coffee_cup (число с оценкой одной чашки капучино) - 7871;
  • seats (количество посадочных мест) - 3611;

Посмотрим какую долю составляют пропуски.

In [18]:
print('Доля пропусков по отношению к общему числу данных:')
display(pd.DataFrame(round(df.isna().mean().sort_values()*100,)))
Доля пропусков по отношению к общему числу данных:
0
name 0.0
category 0.0
address 0.0
district 0.0
lat 0.0
lng 0.0
rating 0.0
chain 0.0
hours 6.0
seats 43.0
avg_bill 55.0
price 61.0
middle_avg_bill 63.0
middle_coffee_cup 94.0

Слишком большая доля пропущенных значений, если их заполнять медианой, то такое большое количество может сильно исказить данные и повлиять на ход исследование, поэтому их стоит оставить без изменения. И продолжить работу с теми данными что есть.

Создадим дополнительные столбцы¶

Создадим столбец street с названиями улиц из столбца с адресом.

In [19]:
words = ['проезд','шоссе','улица','переулок','микрорайон','мкад','проспект','пр.',
         'площадь','аллея','бульвар','набережная','сквер','тупик','линия','территория',
         'квартал','просек','парк','мост']
 
str_pat = r".*,\s*\b([^,]*?(?:{})\b[^,]*)[,$]+".format("|".join(words))
 
df['street'] = df['address'].str.extract(str_pat, flags=re.I)

Добавим столбец is_24/7 с обозначением, что заведение работает ежедневно и круглосуточно (24/7):

  • логическое значение True — если заведение работает ежедневно и круглосуточно;
  • логическое значение False — в противоположном случае.
In [20]:
def is_24_7(time):
    if time == 'ежедневно, круглосуточно':
        return True
    else:
        return False

df['is_24_7'] = df['hours'].apply(is_24_7)
In [21]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8402 entries, 0 to 8401
Data columns (total 16 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               8402 non-null   object 
 1   category           8402 non-null   object 
 2   address            8402 non-null   object 
 3   district           8402 non-null   object 
 4   hours              7866 non-null   object 
 5   lat                8402 non-null   float64
 6   lng                8402 non-null   float64
 7   rating             8402 non-null   float64
 8   price              3315 non-null   object 
 9   avg_bill           3816 non-null   object 
 10  middle_avg_bill    3149 non-null   float64
 11  middle_coffee_cup  535 non-null    float64
 12  chain              8402 non-null   int64  
 13  seats              4793 non-null   float64
 14  street             8267 non-null   object 
 15  is_24_7            8402 non-null   bool   
dtypes: bool(1), float64(6), int64(1), object(8)
memory usage: 992.9+ KB

На этапе предобработки данных:

  • Выявлен и удален 1 неявный дубликат - заведения с одинаковыми наименованими, адресами, категорей, широтой и долготой географической точки.
  • По пропускам - принято решение оставить как есть, так как удаление или заполнение медианным значением такого большого количества данных может исказить ход исследования.
  • добавлены столбец с данными о наименовании улицы и столбец с обозначением работы в ежеднемном и круглосуточном режиме или нет.

Вернуться в начало проекта

Обработка аномалий и редких значений.¶

In [22]:
df.describe()
Out[22]:
lat lng rating middle_avg_bill middle_coffee_cup chain seats
count 8402.000000 8402.000000 8402.000000 3149.000000 535.000000 8402.000000 4793.000000
mean 55.750090 37.608550 4.229910 958.053668 174.721495 0.381219 108.425621
std 0.069667 0.098565 0.470429 1009.732845 88.951103 0.485715 122.845421
min 55.573942 37.355651 1.000000 0.000000 60.000000 0.000000 0.000000
25% 55.704950 37.538632 4.100000 375.000000 124.500000 0.000000 40.000000
50% 55.753384 37.605246 4.300000 750.000000 169.000000 0.000000 75.000000
75% 55.795030 37.664780 4.400000 1250.000000 225.000000 1.000000 140.000000
max 55.928943 37.874466 5.000000 35000.000000 1568.000000 1.000000 1288.000000

Есть значения которые сильно выбиваются от медианных по трем столбцам:

  • middle_avg_bill;
  • middle_coffee_cup;
  • seats.

Стоит разобраться с выбросами и аномалиями по этим столбцам.

In [23]:
sns.set_style('darkgrid')
plt.figure(figsize=(15, 7))

sns.boxplot(x = 'middle_avg_bill', data = df)

plt.title('Ящик с усами по показателю среднего чека заведений Москвы')
plt.xlabel('Сумма среднего чека, руб.')
plt.ylabel('Показатель среднего чека')
plt.show();
No description has been provided for this image
In [24]:
# Посмотрим на данные этих заведений со средним чеком более 10 000руб.
df.query('middle_avg_bill >= 10000')
Out[24]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats street is_24_7
730 чойхона бар,паб москва, дмитровское шоссе, 95а Северный административный округ ежедневно, 10:00–23:00 55.871497 37.543555 4.4 высокие Средний счёт:5000–17000 ₽ 11000.0 NaN 0 49.0 дмитровское шоссе False
5477 гости ресторан москва, шоссе энтузиастов, 52 Восточный административный округ пн,вс 18:00–22:30 55.759088 37.760570 4.1 высокие Средний счёт:5000–15000 ₽ 10000.0 NaN 0 NaN шоссе энтузиастов False
7173 кафе ресторан москва, каширское шоссе, 23, стр. 2 Южный административный округ ежедневно, круглосуточно 55.657450 37.646665 4.1 высокие Средний счёт:20000–50000 ₽ 35000.0 NaN 0 100.0 каширское шоссе True

Наименование заведения "Кафе", категория - ресторан, адрес - Москва, Каширское шоссе, 23, стр. 2, находится в здании НМИЦ онкологии им. Н. Н. Блохина Минздрава России, цены не характерные для заведений общественного питания медицинских учреждений, больше похоже на стоимость исследований в этом НМИЦ.

Аномальных значения 3 (около 0.03% от всех данных), их можно смело удалять.

In [25]:
# Удалим заведения с аномальным средним чеком свыше 10 000 руб.
df.drop(index=df.query('middle_avg_bill >= 10000').index,inplace=True)
In [26]:
# Построим ящик с усами для выявления аномальных значений 
#по средней стоимости чашки капучино
plt.figure(figsize=(15, 7))
sns.boxplot(x = 'middle_coffee_cup', data = df)

plt.title('Ящик с усами по показателю стоимости чашки капучино заведений Москвы')
plt.xlabel('Сумма, руб.')
plt.ylabel('Показатель стоимости чашки капучино')
plt.show();
No description has been provided for this image
In [27]:
# Посмотрим на данные заведения в таблице со значением за чашку капучино более 400 руб.
df.query('middle_coffee_cup >= 400')
Out[27]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats street is_24_7
2857 шоколадница кофейня москва, большая семёновская улица, 27, корп. 1 Восточный административный округ ежедневно, 08:00–23:00 55.782268 37.709022 4.2 средние Цена чашки капучино:230–2907 ₽ NaN 1568.0 1 48.0 большая семёновская улица False

Вероятнее всего это опечатка, в меню данного заведения на сайте указаны цены на кофе от 90 до максимум 290 руб. Стоит исправить эту ошибку на среднее значение - 260.0 руб.

In [28]:
df = df.apply(lambda x: x.replace(
    {'Цена чашки капучино:230–2907 ₽':'Цена чашки капучино:230–290 ₽'}, 
    regex=True
))

df['middle_coffee_cup'] = df['middle_coffee_cup'].replace(1568.0, 260.0)

Теперь посмотрим на ящик с усами по показателю - посадочные места.

In [29]:
# Построим боксплот для показателя количество посадочных мест
plt.figure(figsize=(15, 7))
sns.boxplot(x = 'seats', data = df)

plt.title('Ящик с усами по количеству посадочных мест заведений Москвы')
plt.xlabel('Количество посадочных мест, шт.')
plt.ylabel('Показатель посадки')
plt.show();
No description has been provided for this image
In [30]:
# Загрузим данные заведений с посадкой свыше 1200 мест
#display(df.query('seats > 1200'))

Всего 11 заведений с самой большой посадкой 1288 человек и все они находятся в Западном административном округе Москвы на проспекте Вернадского, при том что имеют разные географические координаты расположения.

Встречаются категории - 4 ресторана, 2 кафе, 2 кофейни, что очень удивительно, обычно кофейни не отличаются высокой посадкой, 2 бара,паба и 1 пиццерия. Предпологается что это заведения с банкетными залами, либо с ресторанными двориками в ТЦ где большая посадка, так же, например, по адресу проспект Вернадского 84 стр. 1 находится филиал РАНХиГС -высшее учебное заведение, скорее всего со столовой, что тоже может давать большую посадку (3 заведения с максимальной посадкой находятся по этому адресу.) Эти заведения выбросы.

Посмотрим какое количество посадочных мест превышает 99 процентилей.

Рассчитаем 99-й перцентиль количества посадочных мест всего по всем заведениям.

In [31]:
# сделаем срез - таблицу без пропущенных значений для оценки 99 перцентиля
df_seats_not_nan = df
df_seats_not_nan['seats'] = df_seats_not_nan['seats'].fillna(0)
In [32]:
print(np.percentile(df_seats_not_nan['seats'], 99)) 
491.0

Среди заведений с указанным количеством посадочных мест, 1% заведений имеет более 490 посадочных мест, остальные данные , менее 1 % - аномалии, их стоит убрать из расчета.

In [33]:
# Избавимся от аномальных значений свыше 490 посадочных мест
df = df.query('seats < 490')
In [34]:
# Для сравнения сколько данных осталось после предобработки 
#выведем количество и долю заведений
after_processing = df['name'].count()
print('Количество заведений в датафрейме после предобработки:', after_processing)
print(
    'Доля удаленных заведений после предобработки:', 
    round((before_processing - after_processing)*100/before_processing, 1), '%')
Количество заведений в датафрейме после предобработки: 8314
Доля удаленных заведений после предобработки: 1.1 %

Аномальные значения составили 1.1%, были удалены. Приступим к дальнейшему анализу данных.

In [35]:
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 8314 entries, 0 to 8401
Data columns (total 16 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               8314 non-null   object 
 1   category           8314 non-null   object 
 2   address            8314 non-null   object 
 3   district           8314 non-null   object 
 4   hours              7787 non-null   object 
 5   lat                8314 non-null   float64
 6   lng                8314 non-null   float64
 7   rating             8314 non-null   float64
 8   price              3275 non-null   object 
 9   avg_bill           3770 non-null   object 
 10  middle_avg_bill    3109 non-null   float64
 11  middle_coffee_cup  529 non-null    float64
 12  chain              8314 non-null   int64  
 13  seats              8314 non-null   float64
 14  street             8179 non-null   object 
 15  is_24_7            8314 non-null   bool   
dtypes: bool(1), float64(6), int64(1), object(8)
memory usage: 1.0+ MB

Шаг 3. Исследовательский анализ данных¶

Анализ категорий заведений¶

Проанализируем виды категорий заведений, количество и долю заведений по категориям.

In [36]:
df_category = df.groupby('category')['name'].count().reset_index()

df_category.columns = ['category', 'count']

category_total = df_category['count'].sum()

df_category['share'] = round(
    df_category['count'] / category_total * 100, 
    1
)

df_category.sort_values(by = 'count', ascending = False)
Out[36]:
category count share
3 кафе 2354 28.3
6 ресторан 2011 24.2
4 кофейня 1397 16.8
0 бар,паб 754 9.1
5 пиццерия 629 7.6
2 быстрое питание 601 7.2
7 столовая 313 3.8
1 булочная 255 3.1
In [37]:
#Зададим определенный цвет каждой категории для всего проекта
category_colors = {
                "кафе": "#4682B4",
                "ресторан": "#FF7F50",
                "кофейня": "#228B22",
                "бар,паб": "#9370DB",
                "пиццерия": "#B22222",
                "быстрое питание": "#8B4513",
                "столовая": "#808080",
                "булочная": "#EE82EE"
}
In [38]:
# Построим груговую диаграмму чтобы отразить доли категорий.
fig = go.Figure(px.pie(
    df_category, 
    values='count', 
    names='category', 
    title="Доли категорий заведений Москвы",
    color = 'category',
    color_discrete_map = category_colors
))
fig.update_layout(width=700, height=450)
fig.show("png");
No description has been provided for this image
In [39]:
# Сформируем столбчатаю диаграмму чтобы оценить количественное 
#распределение заведений по категориям
fig = go.Figure(px.bar(
    df_category, 
    x = 'category', 
    y = 'count', 
    color = "category",
    text = 'share',
    title = 'Распределение заведений по категориям',
    template = 'plotly_white',
    hover_name = "category",
    color_discrete_map = category_colors
))
fig.update_layout(
    width=700, 
    height=400,
    xaxis_title = 'Категории заведений',
    yaxis_title = 'Количество заведений',
    xaxis = {'categoryorder':'total descending'})
fig.show("png");
No description has been provided for this image
  • Представлено восемь категорий заведений.
  • Чаще всего встречаются кафе (28.3%) и рестораны (24.3%), в сумме таких заведений более 50%.
  • На третьем месте по распространенности - кофейни 16.8%.
  • Реже встречаются бары, пабы (9.1%), пиццерии (7.5%) и быстрое питание (7.2%).
  • Среди самых редких категорий можно выделить булочные (3%) и столовые (3.7%).

Вернуться в начало проекта

Анализ количества посадочных мест¶

Исследуем количество посадочных мест в местах по категориям: рестораны, кофейни, пиццерии, бары и так далее.

In [40]:
df.groupby('category')['seats'].describe()
Out[40]:
count mean std min 25% 50% 75% max
category
бар,паб 754.0 66.168435 90.089388 0.0 0.0 40.0 94.0 480.0
булочная 255.0 49.427451 78.660499 0.0 0.0 8.0 70.0 478.0
быстрое питание 601.0 54.863561 83.942469 0.0 0.0 8.0 80.0 450.0
кафе 2354.0 43.903993 72.571346 0.0 0.0 0.0 60.0 481.0
кофейня 1397.0 51.702935 79.571190 0.0 0.0 5.0 80.0 480.0
пиццерия 629.0 59.120827 81.333853 0.0 0.0 30.0 80.0 455.0
ресторан 2011.0 68.055196 87.428239 0.0 0.0 40.0 100.0 481.0
столовая 313.0 46.434505 71.396036 0.0 0.0 4.0 76.0 428.0

После обработки аномальных значений, максимально количество посадочных мест в пределах от 428 ед. до 481 ед.

Медианное значение посадочных мест для заведений по категориям находится в пределах от 0 (кафе) до 40 (рестораны, бары/пабы).

Для лучшего понимания ситуации визуализируем данные и посмотрим на распределение количества посадочных мест.

In [41]:
sns.set_style('darkgrid')
plt.figure(figsize=(13, 6))
sns.stripplot(x = 'category', y = 'seats', data = df);
plt.title(
    'Распределение посадочных мест в зависимостти от категории', 
    fontsize = 16
)
plt.xlabel('Категории заведений', fontsize = 14)
plt.ylabel('Количество посадочных мест', fontsize = 14)
plt.show();
No description has been provided for this image

Для заведений быстрого питания, булочных и столовых не характерна высокая посадка, и значения свыше 300 встречаются редко.

In [42]:
# check
df[['seats']].hist(bins=100, range=(0, 520), figsize=(18,3));
No description has been provided for this image
In [43]:
# подготовим данные для сортировки данных в порядке возрастания медианного значения 
# Подготовим сводную таблицу по категориям и посадочным местам
seats_cat = df.pivot_table(index = 'name', columns='category', values='seats')

# Сохраним наименования категорий в порядке возрастания медианных значений посадки
# для сортировки
sort_index = seats_cat.median().sort_values().index
sort_index
Out[43]:
Index(['кофейня', 'кафе', 'быстрое питание', 'булочная', 'столовая',
       'пиццерия', 'бар,паб', 'ресторан'],
      dtype='object', name='category')
In [44]:
# Посмотрим как распределяются посадочные места без аномалий на графике-скрипка
sns.set_style('darkgrid');
plt.figure(figsize=(13, 6));
custom_palette = [ 
    "green",
    "blue",
    "brown",
    "pink",
    "gray",
    "red",
    "purple",
    "orange",    
]
sns.set_palette(custom_palette);

sns.violinplot(x = 'category', y = 'seats', data = df, cut=0, order = sort_index);
plt.title('Распределение посадочных мест в зависимости от категории', fontsize = 16)
plt.xlabel('Категории заведений', fontsize = 14)
plt.ylabel('Количество посадочных мест', fontsize = 14)
plt.show();
No description has been provided for this image

На графике можно увидеть, что:

  • Большая часть заведений по всем 8 категориям имеет посадку от 0 до 100 мест, чаще чем у других категорий встречаются большие значения посадочных мест у ресторанов, баров/пабов.

  • Имеют схожие распределения посадочных мест категории заведений столовая, булочная, быстрое питание и кофейня. Медианные значения колеблются между 4 и 8 местами, средние значения от 46 до 55 посадочных мест. Не большое количество посадочных мест характерно для заведений таких категорий.

  • Так же схожие распределения посадочных мест, с большим значением среднего и медианы у заведений категории бар/паб, ресторан и пиццерия. Медиана в пределах 30-40, среднее значение - от 59 до 68. Среди этих категорий чаще встречаются заведения с большим количеством посадочных мест, что и характерно для такого типа общепита.

  • Не характерные показатели у категории заведений - кафе. Для заведений категории кафе обычно характерно наличие посадочных мест, но по данным в таблице у большего количества заведений указано 0 посадочных мест, поэтому медианное значение - 0, при этом среднее значение 44.

Вернуться в начало проекта

Анализ сетевых заведений¶

Посмотрим на соотношение сетевых и несетевых заведений.

In [45]:
# заменим числовое обозначение сетевое/несетевое на более читаемое - словестное
df['chain_type'] = df['chain'].apply(lambda x: 'сетевое' if x == 1 else 'несетевое')
In [46]:
chain_type = df.groupby('chain_type', as_index = False)['name'].agg('count')
chain_type['share'] = round(chain_type['name'] / chain_type['name'].sum() *100)
chain_type
Out[46]:
chain_type name share
0 несетевое 5148 62.0
1 сетевое 3166 38.0
In [47]:
# Построим груговую диаграмму 
fig = go.Figure(px.pie(
    chain_type, 
    values='share', 
    names='chain_type', 
    title="Cоотношение сетевых и несетевых заведений питания Москвы",
    #color_discrete_sequence=px.colors.sequential.Reds
))
fig.update_layout(width=700, height=450)
fig.show("png");
No description has been provided for this image
  • 62% заведений относятся к несетевым;
  • 38% заведений относятся к сетевым.

Посмотрим среди каких категорий чаще встречаются сетевые.

In [48]:
chain_category = df.pivot_table(
    index = 'category', 
    values='name', 
    columns = 'chain', 
    aggfunc='count'
).reset_index()

chain_category['total'] = chain_category[0] + chain_category[1]
chain_category['share'] = round(
    chain_category[1] / chain_category['total']*100,
    1
)
chain_category = chain_category.sort_values(
    by='share', 
    ascending=False
)
chain_category
Out[48]:
chain category 0 1 total share
1 булочная 99 156 255 61.2
5 пиццерия 301 328 629 52.1
4 кофейня 686 711 1397 50.9
2 быстрое питание 370 231 601 38.4
6 ресторан 1295 716 2011 35.6
3 кафе 1585 769 2354 32.7
7 столовая 226 87 313 27.8
0 бар,паб 586 168 754 22.3
In [49]:
fig = go.Figure(px.bar(
    chain_category,
    x='category',
    y='share',
    text='share',
    template='plotly_white',                   
    color='category',
    color_discrete_map = category_colors
))
# оформляем график
fig.update_layout(
    title='% сетевых заведений от общего количества заведений Москвы по категориям',
    width=1000, 
    height=400,
    xaxis_title='Категория заведений',
    yaxis_title='Доля сетевых заведений',
)
fig.show("png");
No description has been provided for this image
  • Чаще всего встречаются несетевые заведения 62%.
  • Среди кофейн, пиццерий и булочных больше сетевых заведений чем несетевых.
  • В остальных категориях сетевых почти в два раза меньше, чем несетевых.
  • Реже всего по отношению к общему числу сетевые заведения встречаются среди баров/пабов и столовых.

Вернуться в начало проекта

Анализ топ-15 сетевых заведений¶

Сгруппируем данные по названиям заведений и найдем топ-15 самых распространенных сетей в Москве.

In [50]:
#выделим сетевые заведения в отдельный датафрейм
df_chain = df.query('chain == 1')

# выведем на экран топ-15 сетевых заведений по количественному признаку
top_15_chain = df_chain.groupby('name').agg(
    {'category' : pd.Series.mode, 'chain' : 'count'}
)

#переименуем столбец
top_15_chain = top_15_chain.rename(columns={'chain':'count'})

#отсортируем по убыванию и оставим только 15 наименований заведений
top_15_chain = top_15_chain.sort_values(
    'count', 
    ascending = False
).reset_index().head(15)

top_15_chain
Out[50]:
name category count
0 шоколадница кофейня 118
1 доминос пицца пиццерия 76
2 додо пицца пиццерия 74
3 one price coffee кофейня 70
4 яндекс лавка ресторан 68
5 cofix кофейня 65
6 prime ресторан 49
7 хинкальная кафе 44
8 кофепорт кофейня 42
9 теремок ресторан 38
10 кулинарная лавка братьев караваевых кафе 38
11 чайхана кафе 36
12 cofefest кофейня 32
13 буханка булочная 32
14 му-му кафе 27
In [51]:
#зададим стиль и размер графика
#sns.set_style('dark')
plt.figure(figsize = (15, 9))

#зададим график
sns.barplot(x = 'count', y = 'name', data = top_15_chain)
plt.title(
    'Топ-15 заведений общественного питания Москвы по количественному признаку', 
    fontsize = 16)
plt.xlabel('Количество заведений', fontsize = 14)
plt.ylabel('Наименование заведений', fontsize = 14)
#plt.xticks(rotation = 80)
plt.grid()
plt.show();
No description has been provided for this image

В топ-15 по частоте заведений в Москве вошли такие заведения (в порядке убывания):

  1. шоколадница (кофейня) - 120 заведений;
  2. домино'с пицца (пиццерия) - 76 заведений;
  3. додо пицца (пиццерия) - 74 заведений;
  4. one price coffee (кофейня) - 71 заведений;
  5. яндекс лавка (ресторан) - 69 заведений;
  6. cofix (кофейня) - 65 заведений;
  7. prime (ресторан) - 50 заведений;
  8. хинкальная (кафе) - 44 заведений;
  9. кофепорт (кофейня) - 42 заведений;
  10. кулинарная лавка братьев караваевых (кафе) - 39 заведений;
  11. теремок (ресторан) - 38 заведений;
  12. чайхана (кафе)- 37 заведений;
  13. cofefest (кофейня) - 32 заведений;
  14. буханка (булочная) - 32 заведений;
  15. му-му (кафе) - 27 заведений.

На первом месте в рейтинге кофейня "Шоколадница", которая действительно встречается часто в Моске, эта сеть часто распологает свои кофейни в близи мест с большой проходимостью - возле метро и в торговых центрах, а так же возле бизнес центров. В топ-15 попали в оснвном заведения категорий кофейня и кафе по 4 в каждой категории, так же есть 3 ресторана, 2 пиццерии но очень популярные на 2 и 3 месте по распространенности по городу. Так же в топ-15 попала 1 булочная - "Буханка".

Эти сетевые заведения, обьединяет то что большинство из них часто встречаются в торговых центрах, в бизнес-центрах и в парках или возле них. То есть выбираются многолюдные места с большой проходимостью. Считаю что это важный момент при открытии заведения.

In [52]:
print('Общее количесто сетевых заведений:', df_chain['chain'].count()) 
Общее количесто сетевых заведений: 3166
In [53]:
# Выведем на график доли категорий заведений топ-15
total_top_15 = top_15_chain['count'].sum()
top_15_pivot = top_15_chain.pivot_table(
    index = 'category', 
    values='count', 
    aggfunc='sum'
).reset_index()

top_15_pivot['share'] = round(top_15_pivot['count'] / total_top_15*100, 1)
top_15_pivot = top_15_pivot.sort_values(by='share', ascending=False)
top_15_pivot

#Отобразим общее количество сетевых заведений вошедшие в Топ-15 по категориям
fig = go.Figure(px.bar(
    top_15_pivot,
    x='category',
    y='share',
    template='plotly_white', 
    text='share',
    color='category',
    color_discrete_map = category_colors
))
            
# оформляем график
fig.update_layout(
    title='Доля сетевых заведений Топ-15 по категориям',
    width=700, 
    height=400,
    xaxis_title='Категория заведений',
    yaxis_title='Доля',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image

Заведения попавшие в топ-15 можно объединить по категориям:

  • 40.4% заведений попавших в топ-15 это кофейни, это логично, такой вид заведений не требует больших пллощадей, большой кухни, большого ассортимента и большого колличества персонала что упрощает работу и возможность распространения по городу.
  • 19.2% заведений составляют рестораны;
  • чуть меньше 18.5% это пиццерии;
  • 17.9% в топ-15 попали такие заведения как кафе;
  • меньше всего среди заведений топ-15 это булочные - 4%, хотя среди булочных чаще всего встречаются сетевые заведения.
In [54]:
#сгруппируем данные сетевых заведений Топ-15 по районам Москвы
district_chain = df_chain.groupby(
    ['district', 'category', 'name']
).agg({'chain' : 'count'})

district_chain = district_chain.sort_values(
    'chain', 
    ascending = False
).reset_index()

district_chain = district_chain.rename(columns={'chain':'count'})

district_chain = district_chain[district_chain['name'].isin(
    top_15_chain['name']
)]

#зададим столбчатую диаграмму 
fig = go.Figure(px.bar(
    district_chain,
    x='count',
    y='district',  
    color='category',
    color_discrete_map = category_colors
))
# оформляем график
fig.update_layout(
    title='Распределение категории сетевых заведений Топ-15 по районам Москвы',
    width=900, 
    height=400,
    xaxis_title='Количество заведений',
    yaxis_title='Название района',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image

На Центральный административный округ приходится большая часть сетевых заведений из рейтинка топ-15, больше всего кофейн, ресторанов и кафе. В остальных районах количество заведений распределяется примерно равномерно, но среди категорий пиццерии встречаются чаще чем кафе. Вероятно что на это влияет большое количество спальных районов, в отличии от Центрального округа, где больше прогулочных и тусовочных мест.

В Северо-Западном административном округе меньше популярных сетевых заведений чем в остальных округах.

Мы посмотрели на данные в разрезе топ-15 самых популярных сетевых заведений Москвы, теперь посмотрим в целом по всем заведениям.

Вернуться в начало проекта

Анализ по административным округам¶

Изучим какие административные районы Москвы присутствуют в датасете. Отобразим общее количество заведений и количество заведений каждой категории по районам.

In [55]:
print('В датасете присутствуют следующие административные районы Москвы:')
print(' ')
print(df['district'].value_counts())
print('_'*50)
print('Всего представлено районов Москвы:', df['district'].nunique())
В датасете присутствуют следующие административные районы Москвы:
 
Центральный административный округ         2225
Южный административный округ                890
Северо-Восточный административный округ     888
Северный административный округ             874
Западный административный округ             825
Восточный административный округ            786
Юго-Восточный административный округ        713
Юго-Западный административный округ         704
Северо-Западный административный округ      409
Name: district, dtype: int64
__________________________________________________
Всего представлено районов Москвы: 9
In [56]:
# сгруппируем данные заведений по категориям по районам Москвы
district_cat = df.groupby(['district', 'category']).agg({'name' : 'count'})

district_cat = district_cat.sort_values(
    'name', 
    ascending = False
).reset_index()

district_cat = district_cat.rename(columns={'name':'count'})

# вычислим общее количество заведений по району и посчитаем долю
district_cat['total_district'] = district_cat.groupby(
    'district'
)['count'].transform(sum)

district_cat = district_cat.sort_values(
    ['total_district', 'count'], 
    ascending = False
)

district_cat['share'] = round(
    district_cat['count']/district_cat['total_district']*100, 
    1
)

district_cat
Out[56]:
district category count total_district share
0 Центральный административный округ ресторан 662 2225 29.8
1 Центральный административный округ кафе 459 2225 20.6
2 Центральный административный округ кофейня 425 2225 19.1
3 Центральный административный округ бар,паб 364 2225 16.4
23 Центральный административный округ пиццерия 113 2225 5.1
... ... ... ... ... ...
55 Северо-Западный административный округ пиццерия 40 409 9.8
60 Северо-Западный административный округ быстрое питание 30 409 7.3
66 Северо-Западный административный округ бар,паб 23 409 5.6
68 Северо-Западный административный округ столовая 18 409 4.4
71 Северо-Западный административный округ булочная 12 409 2.9

72 rows × 5 columns

In [57]:
# Построим график распределения категорий заведений по районам Москвы
fig = go.Figure(px.bar(
    district_cat,
    x='share',
    y='district',  
    text='share',
    color='category',
    color_discrete_map = category_colors
))
# оформляем график
fig.update_layout(
    title='Доли категории заведений по районам Москвы',
    width=900, 
    height=400,
    xaxis_title='Доля заведений по категориям',
    yaxis_title='Название района',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image

Больше всего заведений в Центральном районе Москвы, самая популярная категория это рестораны, второе место разделяют кафе и кафейни, далее бары/пабы. Существенно реже встречаются булочные, столовые, быстрое питание и пиццерии.

В остальных административных округах Москвы (за исключением Северо-Западного) количество заведений распределяется почти равномерно, и большей популярностью в этих округах пользуются кафе, второе место занимают рестораны, далее кофейни, остальных категорий представлено значительно меньше и они составляют малую долю.

Меньше всего заведений приходится на долю Северо-Западного района, почти в 5,5 раз меньше чем в Центральном.

Вернуться в начало проекта

Анализ рейтинга заведений¶

Визуализируем распределение средних рейтингов по категориям заведений.

In [58]:
rating_cat = df.groupby(
    'category', 
    as_index=False
)['rating'].agg('mean').round(2)

rating_cat.sort_values(by = 'rating', ascending = False)
rating_cat = rating_cat.sort_values(by = 'rating', ascending = False)

fig = go.Figure(px.bar(
    rating_cat,
    x='rating',
    y='category',
    text='rating',
    template='plotly_white', 
    color='category',
    color_discrete_map = category_colors
))
# оформляем график
fig.update_layout(
    title='Распределение средних рейтингов по категориям заведений',
    width=700, 
    height=400,
    xaxis_title='Средний рейтинг',
    yaxis_title='Название категорий',
)
fig.update_xaxes(range=[4, 4.4])
fig.show("png");
No description has been provided for this image

В целом, различия между рейтингами разных типов заведений не существенные:

  • Между категориями средний рейтинг заведений распределяется между 4.0 и 4.4;
  • самый высокий средний рейтинг у категории бары/пабы 4.39 пунктов;
  • на втором месте категория пиццерия 4.3 пункта, далее ресторан - 4.29 пунктов и кофейня 4.28 пунктов;
  • самый низкий рейтинг у категории быстрое питание - 4.05, немного выше у кафе - 4.12 и столовой 4.21.

Вернуться в начало проекта

Карта заведений¶

Построим фоновую картограмму (хороплет) со средним рейтингом заведений каждого района.

In [59]:
district_rating = df.groupby(
    'district', as_index=False
)['rating'].agg('mean').round(2).sort_values(
    by = 'rating', 
    ascending = False
)

district_rating
Out[59]:
district rating
5 Центральный административный округ 4.38
2 Северный административный округ 4.24
4 Северо-Западный административный округ 4.21
1 Западный административный округ 4.18
8 Южный административный округ 4.18
0 Восточный административный округ 4.17
7 Юго-Западный административный округ 4.17
3 Северо-Восточный административный округ 4.15
6 Юго-Восточный административный округ 4.10
In [60]:
# читаем файл и сохраняем в переменной
with open('/Users/mariapolivanova/Downloads/admin_level_geomap.geojson', 'r') as f:
    geo_json = json.load(f)
    
# загружаем JSON-файл с границами округов Москвы
state_geo = '/Users/mariapolivanova/Downloads/admin_level_geomap.geojson'
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423

# создаём карту Москвы
m = Map(
    location=[moscow_lat, moscow_lng], 
    zoom_start=10, 
    tiles='Cartodb Positron'
)

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
rating_map = folium.Choropleth(
    geo_data=state_geo,
    data=district_rating,
    columns=['district', 'rating'],
    key_on='feature.name',
    fill_color='YlOrBr',
    fill_opacity=0.8,
    legend_name='Средний рейтинг заведений по районам Москвы',
).add_to(m)

#добавим подписи административным округам при наведении курсора на них
rating_map.geojson.add_child(
    folium.features.GeoJsonTooltip(['name',], labels=False)
)

# выводим карту
m
Out[60]:
Make this Notebook Trusted to load map: File -> Trust Notebook
  • Самый высокий средний рейтинг у заведений Центрального округа Москвы - 4.38. Вероятно из-за большой конкуренции заведения стараются держать планку.
  • Самый низкий рейтинг среди заведений Юго-Восточного округа Москвы. Данный район известен крупными рынками, производственным сектором, и более бюджетными ценами на жильё, что может говорить о том что уровень жизни среди населения в этом районе ниже и возможно заведения в этих районах более бюджетные, что может влиять на качество обслуживания, а следовательно и на рейтинги заведений.

Отобразим все заведения датасета на карте с помощью кластеров средствами библиотеки folium

In [61]:
# создаём карту Москвы
m_0 = Map(
    location=[moscow_lat, moscow_lng], 
    zoom_start=10, 
    tiles='Cartodb Positron'
)

# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m_0)

# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
    Marker(
        [row['lat'], row['lng']],
        popup=f"{row['name']} {row['rating']}",
    ).add_to(marker_cluster)

# применяем функцию create_clusters() к каждой строке датафрейма
df.apply(create_clusters, axis=1)

# выводим карту
m_0
Out[61]:
Make this Notebook Trusted to load map: File -> Trust Notebook

По карте можно наблюдать распределение заведений по территории Москвы, больше всего сконцентрировано в Центральном районе.

Далее изучим на каких улицах сконцентрировано большее количество заведений.

Вернуться в начало проекта

Топ-15 улиц по количеству заведений¶

Составим список в которые войдут 15 улиц Москвы с самым большим количеством заведений общественного питания.

In [62]:
# в сводной таблице посчитаем количество заведений по каждой улице
street_df = df.pivot_table(
    index='street', 
    values='name', 
    aggfunc='count'
).sort_values(by='name', ascending=False).head(15)

# сформируем список с наименованием улиц
top_15_street = street_df.index
street_df = street_df.reset_index()
street_df = street_df.rename(columns={'name':'count'})
print('Топ-15 улиц по количеству заведений:')
display(street_df)
Топ-15 улиц по количеству заведений:
street count
0 проспект мира 184
1 профсоюзная улица 122
2 ленинский проспект 101
3 проспект вернадского 97
4 дмитровское шоссе 87
5 каширское шоссе 76
6 варшавское шоссе 75
7 ленинградский проспект 72
8 ленинградское шоссе 69
9 мкад 65
10 люблинская улица 60
11 улица вавилова 53
12 кутузовский проспект 53
13 пятницкая улица 48
14 улица миклухо-маклая 47
In [63]:
#сгруппируем данные по улицам и категориям, 
#оставим только улицы из списка топ-15
street_cat = df.groupby(['street', 'category']).agg({'name' : 'count'})
street_cat = street_cat.sort_values('name', ascending = False).reset_index()
street_cat = street_cat.rename(columns={'name':'count'})
street_cat = street_cat[street_cat['street'].isin(top_15_street)]

# Построим график распределения категорий заведений по топ-15 улицам Москвы
fig = px.bar(
    street_cat,
    x='count',
    y='street',
    text='count',
    color='category',
    color_discrete_map = category_colors
)
# оформляем график
fig.update_layout(
    title='Распределение категории заведений по самым популярным улицам Москвы',
    width=950, 
    height=450,
    xaxis_title='Количество заведений',
    yaxis_title='Название улицы',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image

В топ-15 популярных по количеству заведений улиц вошли в порядке убывания: проспект Мира; Профсоюзная улица; проспект Вернадского; Ленинский проспект; Ленинградский проспект; Дмитровское шоссе; Каширское шоссе; Варшавское шоссе; Ленинградское шоссе; МКАД; Люблинская улица; улица Вавилова; Кутузовский проспект; улица Миклухо-Маклая; Пятницкая улица.

Больше всего заведений общественного питания на проспекте Мира - 184, из которых самая популярная категория - кафе, чуть менее популярны рестораны, кофейни. Меньше всего представлено столовых и булочных.

На остальных улицах из топ-15 в основном схожая ситуация - в тройку самых популярных типов заведений входят кафе, рестораны и кофейни.

Достаточно часто (более 9 заведений на 1 улицу) можно встретить бары/пабы на улицах: проспект Мира; Ленинский проспект; Ленинградский проспект; Пятницкая улица. Хотя это проспекты (за исключением Пятницкой улицы) и имеют достаточно большую протяженность, поэтому, вероятно они достаточно рассредоточены на карте. А вот Пятницкая улица не большая по протяженности и находится в Центральном районе.

Стоит отметить, что в топ-15 улиц попала МКАД - Московская Кольцевая Автомобильная Дорога – это крупнейшая автомобильная развязка в Москве, которая представляет собой кольцо длиной около 109 километров, что превышает протяженность самой длинной улицы Москвы - Варшавское шоссе (длина которого 22,5 км.) почти в 5 раз. При этом МКАД на 10 месте по количеству заведений общественного питания.

Вернуться в начало проекта

Улицы с 1 заведением¶

Проанализируем улицы с 1 заведением.

In [64]:
street_one_object = df.pivot_table(
    index='street', 
    values='name', 
    aggfunc='count'
).sort_values(by='name')

street_one_object = street_one_object[street_one_object['name'] == 1]
street_one_object = street_one_object.index

print('Улиц с одним заведением в Москве:', len(street_one_object))
Улиц с одним заведением в Москве: 426
In [65]:
# создадим сводную таблицу с улицами на которых по 1 заведению для вывода на карту Москвы
one_obj_street = df[df['street'].isin(street_one_object)]
one_obj_street = one_obj_street.groupby(
    ['street', 'name', 'category', 'lat', 'lng']
).agg({'chain' : 'count'})

one_obj_street = one_obj_street.reset_index()
one_obj_street = one_obj_street.rename(columns={'chain':'count'})
In [66]:
# создаём карту Москвы
m_1 = Map(
    location=[moscow_lat, moscow_lng], 
    zoom_start=10, 
    tiles='Cartodb Positron'
)

# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m_1)

def create_clusters(row):
    # сохраняем URL-адрес изображения со значком с icons8,
    # это путь к файлу на сервере icons8
    icon_url = 'https://img.icons8.com/?size=100&id=MU6RngCQ3iWG&format=png'  
    # создаём объект с собственной иконкой размером 30x30
    icon = CustomIcon(icon_url, icon_size=(30, 30))
    
    # создаём маркер с иконкой icon и добавляем его в кластер
    Marker(
        [row['lat'], row['lng']],
        popup=f"{row['name']} {row['category']}",
        icon=icon,
    ).add_to(marker_cluster)

# применяем функцию для создания кластеров к каждой строке датафрейма
one_obj_street.apply(create_clusters, axis=1)

# выводим карту
m_1
Out[66]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [67]:
one_obj_pivot = one_obj_street.groupby(
    'category'
).agg({'name' : 'count'}).sort_values('name', ascending = False).reset_index()
one_obj_pivot = one_obj_pivot.rename(columns={'name':'count'})

one_obj_total = one_obj_pivot['count'].sum()

one_obj_pivot['share'] = round(one_obj_pivot['count'] / one_obj_total * 100, 1)

one_obj_pivot.sort_values(by = 'count', ascending = False)


# Построим график распределения заведений по категориям среди улиц с 1 заведением
fig = px.bar(
    one_obj_pivot,
    x='category',
    y='share',
    text='share',
    color = 'category',
    color_discrete_map = category_colors
)
# оформляем график
fig.update_layout(
    title='Доля заведений по категориям среди улиц с 1 заведением',
    width=700, 
    height=400,
    xaxis_title='Категория заведений',
    yaxis_title='Доля заведений',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image
  • По всей Москве встречаются улицы с одним заведением, то есть такое явление нормально.
  • Чаще всего среди одиночных заведений встречаются кафе, рестораны и кофейни.
  • Реже всех встречаются заведения категории булочная.
  • Вероятно что улицы не длинные и находятся в спальных районах.

Вернуться в начало проекта

Анализ ценового диапазона¶

Значения средних чеков заведений хранятся в столбце middle_avg_bill. Эти числа показывают примерную стоимость заказа в рублях, которая чаще всего выражена диапазоном.

Посчитаем медиану этого столбца для каждого района. Используем это значение в качестве ценового индикатора района.

Построим фоновую картограмму (хороплет) с полученными значениями для каждого района.

In [68]:
distr_avg_bill = df.query(
    'middle_avg_bill != "NaN"'
).groupby('district')['middle_avg_bill'].median()

distr_avg_bill = distr_avg_bill.reset_index().sort_values(
    by = 'middle_avg_bill', 
    ascending = False
)

distr_avg_bill
Out[68]:
district middle_avg_bill
1 Западный административный округ 1000.0
5 Центральный административный округ 1000.0
4 Северо-Западный административный округ 700.0
2 Северный административный округ 650.0
7 Юго-Западный административный округ 600.0
0 Восточный административный округ 550.0
3 Северо-Восточный административный округ 500.0
8 Южный административный округ 500.0
6 Юго-Восточный административный округ 450.0
In [69]:
# создаём карту Москвы
m_2 = Map(
    location=[moscow_lat, moscow_lng], 
    zoom_start=10, 
    tiles='Cartodb Positron'
)

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
avg_bill_map = Choropleth(
    geo_data=state_geo,
    data=distr_avg_bill,
    columns=['district', 'middle_avg_bill'],
    key_on='feature.name',
    fill_color = 'YlOrBr',
    legend_name='Средний чек заведений по районам',
    text='middle_avg_bill'
).add_to(m_2)

# добавим подписи административным округам 
# отображающиеся при наведении курсора на них
avg_bill_map.geojson.add_child(
    folium.features.GeoJsonTooltip(['name',], labels=False)
)

# выводим карту
m_2
Out[69]:
Make this Notebook Trusted to load map: File -> Trust Notebook
  • Самый высокий показатель среднего чека в 1 000 руб. по районам Москвы у Центрального административного округа и Западного административного округа. Эта сумма в два раза больше чем у районов с наименьшим показателем среднего чека.
  • Самые низкие средние чеки в Северо-Восточномй административном округе (500.0 руб.); Южном административном округе (500.0 руб.) и Юго-Восточном административном округе.
  • В остальных районах медианный показатель среднего чека в диапазоне 550 - 700 руб., что на 30-42.5% меньше чем Центральном и Западном районах.

Стоит иметь ввиду, что существенная разность в соотношении заведений по округам Москвы, может оказывать влияние на величину среднего чека, так преобладающая доля ресторанов и пабов в ЦАО с высокими чеками повышает медианное значение, по сравнению, например, с не большим количеством заведений в ЮАО.

Так же на цены в заведениях сильно влияет стоимость аренды помещений, ведь в Центральном и Западном районах Москвы стоимость аренды помещений может встречаться значительно выше чем в более отдаленных районах. В Западном районе Москвы больше элитной недвижимости и бизнес-центров, что делает этот район более престижным, и цены на аренду помещений выше чем в менее пристижных районах.

Вернуться в начало проекта

Анализ круглосуточных заведений¶

Исследуем круглосуточные и не круглосуточные заведения.

In [70]:
df_24_7 = df.query('is_24_7 == True')

print('Всего круглосуточных заведений в датафрейме:', df_24_7['name'].count())

print(f'Доля круглосуточных заведений: {df_24_7.name.count()/df.name.count():.1%}')
Всего круглосуточных заведений в датафрейме: 726
Доля круглосуточных заведений: 8.7%
In [71]:
pivot_24_7 = df_24_7.groupby(['category']).agg({
    'chain' : 'count', 
    'rating' : 'median', 
    'seats' : 'mean',
    'middle_avg_bill' : 'mean'
})
pivot_24_7 = pivot_24_7.reset_index()
pivot_24_7 = pivot_24_7.rename(columns={'chain':'count'})
pivot_24_7 = pivot_24_7.sort_values(by = 'count', ascending = False)
pivot_24_7.style.format({
    'middle_avg_bill' : '{:.0f}', 
    'seats' : '{:.0f}', 
    'rating' : '{:.1f}'
})
Out[71]:
category count rating seats middle_avg_bill
3 кафе 266 4.1 36 562
2 быстрое питание 150 4.1 42 280
6 ресторан 132 4.3 59 1014
4 кофейня 59 4.2 57 1423
0 бар,паб 52 4.4 64 1184
5 пиццерия 31 4.2 49 475
1 булочная 24 4.2 82 360
7 столовая 12 4.2 36 312
In [72]:
plt.figure(figsize=(10, 5))
custom_palette = [ 
    "blue",
    "brown",
    "orange",
    "green",
    "purple",
    "red",
    "pink",
    "gray"
]

sns.set_palette(custom_palette)
sns.barplot(
    x='category', 
    y='count', 
    data=pivot_24_7
)
plt.title('Распределение  круглосуточных заведений Москвы по категориям')
plt.xlabel('Категории заведений')
plt.ylabel('Количество заведений')
plt.show();
No description has been provided for this image
In [73]:
district_24_7 = df_24_7.groupby(['district', 'category']).agg({'chain' : 'count'})
district_24_7 = district_24_7.reset_index()
district_24_7 = district_24_7.rename(columns={'chain':'count'})
district_24_7 = district_24_7.sort_values(by = 'count', ascending = False)

fig = px.bar(
    district_24_7,
    x='count',
    y='district',
    color='category',
    text='count',
    color_discrete_map = category_colors
)
# оформляем график
fig.update_layout(
    title='Распределение категории круглосуточных заведений по районам Москвы',
    width=950, 
    height=400,
    xaxis_title='Количество круглосуточных заведений',
    yaxis_title='Название района',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image
  • Круглосуточные заведения составляют 8.7% от всех заведений Москвы из датафрейма.
  • Чаще всего среди круглосуточных заведений можно встретить кафе, быстрое питание и рестораны.
  • Реже всего встречаются круглосуточные столовые.
  • Средний рейтинг между категориями круглосуточных заведений варьируется в пределах 4.1 и 4.4 (самый высокий у категории бар,паб).
  • Самая высокий средний показатель посадки среди категорий круглосуточных заведений у булочных (86 мест).
  • Самый высокий седний чек среди круглосуточных заведений Москвы у ресторанов, кофейн и баров,пабов.
  • Больше всего круглосуточных заведений в Центральном округе Москвы, а меньше всего - в Северо-Западном.

Вернуться в начало проекта

Анализ заведений с низкими рейтингами¶

Построим графики бокс плот - отражающие статистические величины, для определения категории низких рейтингов.

In [74]:
# Подготовим сводную таблицу по категориям и рейтингам
rating_cat = df.pivot_table(index = 'name', columns='category', values='rating')

# Сохраним наименования категорий в порядке возрастания медианных рейтингов для сортировки
index_sort = rating_cat.mean().sort_values().index
index_sort
Out[74]:
Index(['быстрое питание', 'кафе', 'столовая', 'булочная', 'ресторан',
       'пиццерия', 'кофейня', 'бар,паб'],
      dtype='object', name='category')
In [75]:
# построим ящик с усами по рейтингам заведений с разбивкой по категориям 
plt.figure(figsize = (15, 7))
custom_palette = [ 
    "brown",
    "blue",
    "gray",
    "pink",
    "orange",
    "red",
    "green",
    "purple",   
]

sns.set_palette(custom_palette)
ax = sns.boxplot(x='category', y='rating', data=df, order=index_sort) 
plt.title('Распределение рейтинговых оценок по категориям заведений Москвы', fontsize = 17)
plt.xlabel('Категории заведений')
plt.ylabel('Рейтинг заведений')
plt.show();
No description has been provided for this image
  • У всех заведений в датафрейме выставлены рейтинги
  • Максимальное значение 5, минимальное 1.
  • Для большинства категорий выбросами являются рейтинги меньше 3.5, для всех категорий ниже 3 баллов.

Рассмотрим заведения с балом меньше трех единиц.

In [76]:
low_rating = df.query(
    'rating <= 3.0'
).groupby(['category']).agg({'name' : 'count', 'middle_avg_bill' : 'median'})

low_rating = low_rating.reset_index()
low_rating = low_rating.sort_values(by = 'name', ascending = False)

low_rating
Out[76]:
category name middle_avg_bill
3 кафе 118 750.0
6 ресторан 37 NaN
2 быстрое питание 35 325.0
4 кофейня 18 NaN
5 пиццерия 9 900.0
0 бар,паб 8 NaN
7 столовая 7 237.5
1 булочная 2 325.0
  • Чаще всего среди заведений с низким рейтингом встречаются кафе.
  • Реже всего среди заведений низкий рейтинг бывает у булочных.
  • У заведений с низким рейтингом ниже и средний чек, что логично.
  • Среди заведений с низким рейтингом смый высокий чек у заведений категории пиццерия.

Вернуться в начало проекта

Вывод:¶

По изученным данным заведений Москвы можно выделить:

  1. Всего представлено восемь категорий заведений: чаще всего встречаются кафе (28.3%) и рестораны (24.3%), на третьем месте по распространенности кофейни 16.8%. Среди редких категорий можно выделить булочные (3%) и столовые(3.7%).

  2. Большая часть заведений по всем 8 категориям имеет посадку от 0 до 100 мест, чаще чем у других категорий встречаются большие значения посадочных мест у ресторанов, баров/пабов. У заведений категорий столовая, булочная, быстрое питание и кофейня, медианные значения колеблются между 4 и 8 местами, средние значения от 46 до 55 посадочных мест. Не большое количество посадочных мест характерно для заведений таких категорий.

Не характерные показатели у категории заведений - кафе. Для заведений категории кафе обычно характерно наличие посадочных мест, но по данным в таблице у большего количества заведений указано 0 посадочных мест, поэтому медианное значение - 0, при этом среднее значение 44.

  1. Чаще всего встречаются несетевые заведения их 62% в датафрейме. Среди кофейн, пиццерий и булочных больше сетевых заведений чем несетевых. В остальных категориях сетевых почти в два раза меньше, чем несетевых. Реже всего по отношению к общему числу сетевые заведения встречаются среди баров/пабов и столовых.

  2. В топ-15 по частоте заведений в Москве На первом месте в рейтинге кофейня "Шоколадница", которая действительно встречается часто в Моске, эта сеть часто распологает свои кофейни в близи мест с большой проходимостью - возле метро и в торговых центрах, а так же возле бизнес центров и вокзалов. В топ-15 попали в оснвном заведения категорий кофейня и кафе по 4 в каждой категории, так же есть 3 ресторана, 2 пиццерии но очень популярные на 2 и 3 месте по распространенности по городу. Так же в топ-15 попала 1 булочная - "Буханка".

Эти сетевые заведения, обьединяет то что большинство из них часто встречаются в торговых центрах, в бизнес-центрах и в парках или возле них. То есть выбираются многолюдные места с большой проходимостью. Считаю что это важный момент при открытии заведения.

Большая часть сетевых заведений из рейтинка топ-15 приходится на Центральный административный округ, где представлены в оснавном кофейни, рестораны и кафе. В остальных районах количество заведений распределяется примерно равномерно, но среди категорий пиццерии встречаются чаще чем кафе. Вероятно что на это влияет большое количество спальных районов, в отличии от Центрального округа, где больше прогулочных и тусовочных мест.

  1. Всего в датасете представлено 9 административных округов Москвы. Больше всего заведений в Центральном районе Москвы, самая популярная категория это рестораны, второе место разделяют кафе и кофейни, далее бары/пабы. Существенно реже встречаются булочные, столовые, быстрое питание и пиццерии. Меньше всего заведений приходится на долю Северо-Западного района, почти в 5,5 раз меньше чем в Центральном.

В остальных административных округах Москвы (за исключением Северо-Западного) количество заведений распределяется почти равномерно, и большей популярностью в этих округах пользуются кафе, второе место занимают рестораны, далее кофейни, остальных категорий представлено значительно меньше и они составляют малую долю.

  1. В целом, различие между средними рейтингами разных категорий заведений не существенное, распределяется между 4.0 и 4.4. Самый высокий средний рейтинг у категории бары/пабы 4.39 пунктов, на втором месте категория пиццерия 4.3 пункта, далее ресторан - 4.29 пунктов и кофейня 4.28 пунктов. Самый низкий рейтинг у категории быстрое питание - 4.05, немного выше у кафе - 4.12 и столовой 4.21.

  2. Если рассматривать средний рейтинг среди районов Москвы, то самый высокий средний рейтинг у заведений Центрального округа Москвы - 4.38. Вероятно из-за большой конкуренции заведения стараются держать планку. Самый низкий рейтинг среди заведений Юго-Восточного округа Москвы. Данный район известен крупными рынками, производственным сектором, и более бюджетными ценами на жильё, что может говорить о том что уровень жизни среди населения в этом районе ниже и возможно заведения в этих районах более бюджетные, что может влиять на качество обслуживания, а следовательно и на рейтинги заведений.

  3. Среди самых популярных по количеству заведений общественного питания первое место занимает проспект Мира - 184 заведения, из которых самая популярная категория - кафе, чуть менее популярны рестораны, кофейни. Меньше всего представлено столовых и булочных. На остальных улицах из топ-15 в основном схожая ситуация - в тройку самых популярных типов заведений входят кафе, рестораны и кофейни.

Достаточно часто (более 9 заведений на 1 улицу) можно встретить бары/пабы на улицах: проспект Мира; Ленинский проспект; Ленинградский проспект; Пятницкая улица. Хотя это проспекты (за исключением Пятницкой улицы) и имеют достаточно большую протяженность, поэтому, вероятно они достаточно рассредоточены на карте. А вот Пятницкая улица не большая по протяженности и находится в Центральном районе.

  1. Самый высокий показатель среднего чека в 1 000 руб. по районам Москвы у Центрального административного округа и Западного административного округа. Эта сумма в два раза больше чем у районов с наименьшим показателем среднего чека. Самые низкие средние чеки в Северо-Восточномй административном округе (500.0 руб.); Южном административном округе (500.0 руб.) и Юго-Восточном административном округе. В остальных районах медианный показатель среднего чека в диапазоне 575 - 700 руб., что на 30-42.5% меньше чем Центральном и Западном районах.

На цены в заведениях сильно влияет стоимость аренды помещений, ведь в Центральном и Западном районах Москвы стоимость аренды помещений очень высока. В Западном районе Москвы больше элитной недвижимости и бизнес-центров, что делает этот район более престижным, вот и цены на аренду помещений выше чем в менее пристижных районах.

  1. Круглосуточные заведения составляют 8.7% от всех заведений Москвы из датафрейма. Чаще всего среди круглосуточных заведений можно встретить кафе, быстрое питание и рестораны. Реже всего встречаются круглосуточные столовые. Средний рейтинг между категориями круглосуточных заведений варьируется в пределах 4.1 и 4.4 (самый высокий у категории бар,паб). Самая высокий средний показатель посадки среди категорий круглосуточных заведений у булочных (86 мест). Самый высокий средний чек среди круглосуточных заведений Москвы у ресторанов, кофейн и баров,пабов. Больше всего круглосуточных заведений в Центральном округе Москвы, а меньше всего - в Северо-Западном как и в целом по всем заведениям.

  2. Чаще всего среди заведений с низким рейтингом встречаются кафе. Реже всего среди заведений низкий рейтинг бывает у булочных. У заведений с низким рейтингом ниже и средний чек, что логично. Среди заведений с низким рейтингом смый высокий чек у заведений категории пиццерия.

Вернуться в начало проекта

Шаг 4. Детализированное исследование: открытие кофейни¶

Основателям фонда «Shut Up and Take My Money» не даёт покоя успех сериала «Друзья». Их мечта — открыть такую же крутую и доступную, как «Central Perk», кофейню в Москве.

Будем считать, что заказчики не боятся конкуренции в этой сфере, ведь кофеен в больших городах уже достаточно.

Попробуем определить, осуществима ли мечта клиентов.

Анализ количества и расположения кофейн¶

Посмотрим сколько всего кофейн Москвы представлено в полученном для исследования датафрейме и как они распределены по районам Москвы.

In [77]:
# выделим кофейни в отдельный датафрейм
df_coff = df.query('category == "кофейня"')
print('Всего количество кофейн:', df_coff.shape[0])

# В сводной таблице сгруппируем кофейни по районам и посчитаем их количество
coff_pivot = df_coff.groupby(['district']).agg({'name' : 'count'})
coff_pivot = coff_pivot.reset_index()
coff_pivot = coff_pivot.rename(columns={'name':'count'})
coff_pivot = coff_pivot.sort_values(by = 'count', ascending = False)

# Построим столбчатый график распределения кофейн по районам
fig = px.bar(
    coff_pivot,
    x='count',
    y='district',
    text='count',
    color = 'district'
)
# оформляем график
fig.update_layout(
    title='Распределение кофейн по районам Москвы',
    width=950, 
    height=400,
    xaxis_title='Количество кофейн',
    yaxis_title='Название района',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
Всего количество кофейн: 1397
No description has been provided for this image
In [78]:
# создаём карту Москвы
m_3 = Map(location=[moscow_lat, moscow_lng], zoom_start=10, tiles='Cartodb Positron')

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
count_map = folium.Choropleth(
    geo_data=state_geo,
    data=coff_pivot,
    columns=['district', 'count'],
    key_on='feature.name',
    fill_color='YlOrBr',
    fill_opacity=0.8,
    legend_name='Количество кофейн по районам Москвы',
).add_to(m_3)

#добавим подписи административным округам при наведении курсора на них
count_map.geojson.add_child(folium.features.GeoJsonTooltip(['name',], labels=False))


# создаём пустой кластер, добавляем его на карту
marker_cluster_2 = MarkerCluster().add_to(m_3)

# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
    Marker(
        [row['lat'], row['lng']],
        popup=f"{row['name']} {row['rating']}",
    ).add_to(marker_cluster_2)

# применяем функцию create_clusters() к каждой строке датафрейма
df_coff.apply(create_clusters, axis=1)

# выводим карту
m_3
Out[78]:
Make this Notebook Trusted to load map: File -> Trust Notebook
  • Всего 1413 кофейн.
  • Больше всего кофейн в Центральном районе Москвы, их количество превышает более чем в два раза над остальными района - 428.
  • В остальных административных округах кофейн менее 200 заведений на район, лидеры среди них - Северный, Северо-Восточный и Западный административные округа..
  • Меньше всего кофейн (62) в Северо-Западном административном округе.
  • По карте сложно определить особенности расположения кофейн, но в целом, они встречаются в первых этажах многоквартирных домов, в торговых центрах, в бизнес центрах, возле парковых зон и метро.

Вернуться в начало проекта

Исследование часов работы кофейн¶

Посмотрим как часто встречаются круглосуточные кофейни.

In [79]:
# заменим булевое обозначение в столбце is_24_7 -  True/False на более читаемое - словестное
df_coff['work_hours_type'] = df_coff['is_24_7'].apply(
    lambda x: 'не круглосуточно' if x == False else 'круглосуточно'
)

hours_type = df_coff.groupby('work_hours_type', as_index = False)['name'].agg('count')
hours_type['share'] = round(hours_type['name'] / hours_type['name'].sum() *100, 1)
hours_type
Out[79]:
work_hours_type name share
0 круглосуточно 59 4.2
1 не круглосуточно 1338 95.8
In [80]:
# Построим груговую диаграмму 
fig = go.Figure(
    data=[go.Pie(labels=hours_type['work_hours_type'], values=hours_type['share'])]
)
fig.update_layout(
    title='Cоотношение круглосуточных и не круглосуточных кофейн Москвы', 
    width=700, 
    height=400,
    annotations=[dict(
        x=1.15, 
        y=1.05,
        text='Время работы:',
        showarrow=False
    )]
)
fig.show("png");
No description has been provided for this image
In [81]:
# сгруппируем данные по районам, только для круглосуточных кофейн
coff_type = df_coff.query(
    'is_24_7 == True'
).groupby(['district'], as_index=False).agg({
    'name' : 'count',
    'rating' : 'mean'
}).sort_values(by='name', ascending = False).reset_index()

coff_type = coff_type.rename(columns={'name':'count'})

#зададим столбчатую диаграмму 
fig = px.bar(coff_type,
             x='count',
             y='district',  
             text='count',
             color='rating'
)

# оформляем график
fig.update_layout(
    title='Распределение круглосуточных кофейн по районам Москвы',
    width=700, 
    height=400,
    xaxis_title='Количество заведений',
    yaxis_title='Название района',
    yaxis={'categoryorder':'total ascending'}
)
fig.show("png");
No description has been provided for this image
In [82]:
# создадим сводную таблицу с круглосуточными кофейнями для вывода на карту Москвы
coff_for_map = df_coff.query('is_24_7 == True')
coff_for_map = coff_for_map.groupby(
    ['name', 'lat', 'lng']
).agg({'chain' : 'count', 'rating' : 'median'})
coff_for_map = coff_for_map.reset_index()
coff_for_map = coff_for_map.rename(columns={'chain':'count'})


# создаём карту Москвы
m_4 = Map(
    location=[moscow_lat, moscow_lng], 
    zoom_start=10, 
    tiles='Cartodb Positron'
)

# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m_4)

def create_clusters(row):
    # сохраняем URL-адрес изображения со значком с icons8,
    # это путь к файлу на сервере icons8
    icon_url = 'https://img.icons8.com/?size=160&id=Ztce0zxzCUVT&format=png'  
    # создаём объект с собственной иконкой размером 30x30
    icon = CustomIcon(icon_url, icon_size=(30, 30))
    
    # создаём маркер с иконкой icon и добавляем его в кластер
    Marker(
        [row['lat'], row['lng']],
        popup=f"{row['name']} {row['rating']}",
        icon=icon,
    ).add_to(marker_cluster)

# применяем функцию для создания кластеров к каждой строке датафрейма
coff_for_map.apply(create_clusters, axis=1)

# выводим карту
m_4
Out[82]:
Make this Notebook Trusted to load map: File -> Trust Notebook
  • Круглосуточных кофейн в Москве очень мало - 4.2% от общего числа кофейн.
  • Большая часть круглосуточных кофейн находятся в Центральном округе, они расположились в доль садового кольца, на ул. Новый Арбат, Большая Никитская и других проходных улицах.
  • В остальных районах круглосуточные кофейни большая редкость, чаще всего они распологаются вдоль больших проспектов, вероятно больше расчитаны на таксистов.
  • Самый высокий средний рейтинг у кофейн расположенных в Центральном, Восточном и Юго-Западном районах.
  • Самый низкий рейтинг у единственной круглосуточной кофейни в Юго-Восточном административном округе.

Вернуться в начало проекта

Анализ рейтингов кофейн и их распределение по районам¶

Посмотрим на рейтинги и распространение по районам вцелом по всем кофейням Москвы.

In [83]:
rating_coff = df_coff.groupby(
    'district', 
    as_index=False
)['rating'].agg('mean').round(2)

rating_coff = rating_coff.sort_values(
    by = 'rating', 
    ascending = False
)

fig = px.bar(
    rating_coff,
    x='rating',
    y='district',
    text='rating',
    template='plotly_white', 
    color='rating'                   
)
# оформляем график
fig.update_layout(
    title='Распределение средних рейтингов кофейн по районам Москвы',
    width=700, 
    height=400,
    xaxis_title='Средний рейтинг',
    yaxis_title='Район Москвы'
)
fig.update_xaxes(range=[4.1, 4.4])
fig.show("png");
No description has been provided for this image
  • В целом разброс в значениях между средними рейтингами кофейн по районам Москвы не большой между 4.2 и 4.34.
  • Самый высокий средний рейтинг у кофейн расположенных в Центральном административном округе - 4.34 пункта и Северо-Западном округе.
  • Самый низкий средний рейтинг у кофейн в Западном районе - 4.2

Вернуться в начало проекта

Анализ цен при открытии¶

Выясним на какую стоимость чашки капучино стоит ориентироваться при открытии кофейни.

In [84]:
df_coff.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1397 entries, 3 to 8396
Data columns (total 18 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               1397 non-null   object 
 1   category           1397 non-null   object 
 2   address            1397 non-null   object 
 3   district           1397 non-null   object 
 4   hours              1382 non-null   object 
 5   lat                1397 non-null   float64
 6   lng                1397 non-null   float64
 7   rating             1397 non-null   float64
 8   price              470 non-null    object 
 9   avg_bill           711 non-null    object 
 10  middle_avg_bill    196 non-null    float64
 11  middle_coffee_cup  515 non-null    float64
 12  chain              1397 non-null   int64  
 13  seats              1397 non-null   float64
 14  street             1367 non-null   object 
 15  is_24_7            1397 non-null   bool   
 16  chain_type         1397 non-null   object 
 17  work_hours_type    1397 non-null   object 
dtypes: bool(1), float64(6), int64(1), object(10)
memory usage: 197.8+ KB

Только у 521 кофейни из 1413 в датафрейме заполнены сведения о стоимости чашки капучино, исследуем данные о стоимости чашки капучино по этим кофейням.

In [85]:
price_cof_cup = df_coff.query('middle_coffee_cup > 0')

price_cof_cup['middle_coffee_cup'].describe()
Out[85]:
count    515.000000
mean     172.524272
std       65.879982
min       60.000000
25%      124.500000
50%      170.000000
75%      225.000000
max      375.000000
Name: middle_coffee_cup, dtype: float64

В целом по Москве:

  • Среднее значение за чашку капучино - 172 руб.
  • Медианное значение - 170 руб.
  • Максимальное значение 375 руб., а минимальная стоимость чашки капучино - 60 руб.
In [86]:
price_cof_pivot = price_cof_cup.groupby(
    'middle_coffee_cup', 
    as_index=False
)['name'].agg('count')

fig = px.histogram(
    price_cof_pivot,
    x = 'middle_coffee_cup',
    y = 'name',
    title = 'Распределение количества заведений по стоимости за чашку кофе',
    nbins = 200,
)
fig.update_xaxes(title_text = 'Средняя стоимость чашки капучино')
fig.update_yaxes(title_text = 'Количество заведений')
fig.show("png");
No description has been provided for this image

На графике наблюдается скачок в цене в размере 256 руб за чашку капучино, посмотрим, что это за кофейни с одинаковый средним чеком.

In [87]:
df_256 = price_cof_cup.query('middle_coffee_cup == 256')
print(df_256['name'].value_counts())
шоколадница    42
Name: name, dtype: int64

Все 42 кофейни со стоимостью чашки капучино 256 руб - это сетевые кофейни "Шоколадница", где равный средний чек. Для сетевых заведений характерно придерживаться единой ценовой политики на меню.

In [88]:
# Выведем среднюю стоимость чашки капучино по районам
district_cup = df_coff.groupby(
    'district', 
    as_index=False
)['middle_coffee_cup'].agg('mean').round().sort_values(
    'middle_coffee_cup', 
    ascending=False
)

district_cup
Out[88]:
district middle_coffee_cup
1 Западный административный округ 189.0
5 Центральный административный округ 188.0
7 Юго-Западный административный округ 184.0
2 Северный административный округ 166.0
4 Северо-Западный административный округ 166.0
3 Северо-Восточный административный округ 165.0
8 Южный административный округ 158.0
6 Юго-Восточный административный округ 151.0
0 Восточный административный округ 143.0
In [89]:
# создаём карту Москвы
m_5 = Map(
    location=[moscow_lat, moscow_lng], 
    zoom_start=10, 
    tiles='Cartodb Positron'
)

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
cup_map = Choropleth(
    geo_data = state_geo,
    data = district_cup,
    columns = ['district', 'middle_coffee_cup'],
    key_on = 'feature.name',
    fill_color = 'YlOrBr',
    legend_name = 'Средняя цена чашки кофе по районам Москвы',
).add_to(m_5)

#добавим подписи административным округам при наведении курсора на них
cup_map.geojson.add_child(
    folium.features.GeoJsonTooltip(['name',], labels=False)
)

# выводим карту
m_5
Out[89]:
Make this Notebook Trusted to load map: File -> Trust Notebook
  • Средняя цена за чашку капучино по Москве колеблется по районам в пределах от 143 руб. до 189 руб.
  • Самые высокие цены на капучино в Западном (189 руб.), Центральном (188 руб.) и Юго-Западном (184 руб.) округах.
  • Самые низкие цены на чашку капучино в Восточном -143 руб., Юго-Восточном округе - 151 руб и и Южном административном округе - 158 руб.

При открытии кафе стоит ориентироваться на среднюю стоимость чашечки капучино внутри того округа где планируется открывать кофейню, потому что чашка капучино это как маячок для посетителей кофейни, если будет завышена цена, то это может создать впечатление что в заведении слишком высокие цены и средний посетитель не будет заходить к вам в заведение. Если сделать слишком низкую цену на капучино, то это может быть сигналом о том что в данном заведении подают некачественный кофе, и будет предвзятое отношение.

Вернуться в начало проекта

Топ-10 сетевых кофейн¶

Рассмотрим какое количество сетевых кофейн среди остальных по Москве. Изучим топ-10 сетевых кофейн по параметрам - посадки, рейтинга, ценовой категории.

In [90]:
chain_coff = df_coff.groupby(
    'chain_type', 
    as_index = False
)['name'].agg('count')

chain_coff['share'] = round(
    chain_coff['name'] / chain_coff['name'].sum() *100
)
chain_coff
Out[90]:
chain_type name share
0 несетевое 686 49.0
1 сетевое 711 51.0
In [91]:
# Отразим на круговой диаграмме доли сетевых кофейн
fig = px.pie(
    chain_coff, 
    values='name', 
    names='chain_type'
)
fig.update_layout(
    title='Доля сетевых кофейн Москвы', 
    width=700, 
    height=400,
    annotations=[dict(
        x=1.15, 
        y=1.05,
        text='Категория',
        showarrow=False
    )]
)
fig.show("png");
No description has been provided for this image
In [92]:
# уберем 1% аномальных значений посадочных мест
df_coff = df_coff[~(df_coff['seats'] > np.nanpercentile(df_coff['seats'], 99))]
In [93]:
#выделим сетевые заведения в отдельный датафрейм
chain_coffe_hous = df_coff.query('chain == 1')

# выведем на экран топ-15 сетевых заведений по количественному признаку
top_10_coffe = chain_coffe_hous.groupby(['name']).agg({
    'chain' : 'count',
    'rating' : 'mean',
    'seats' : 'median',
    'price' : 'first'
})

# переименуем столбец
top_10_coffe = top_10_coffe.rename(columns={'chain':'count'})

# округлим значение рейтинга до 1 значения после запятой
top_10_coffe['rating'] = round(top_10_coffe['rating'],  1)

# отсортируем по убыванию и оставим только 10 наименований заведений
top_10_coffe = top_10_coffe.sort_values(
    'count', 
    ascending = False
).reset_index().head(10)

top_10_coffe
Out[93]:
name count rating seats price
0 шоколадница 116 4.2 61.5 средние
1 one price coffee 69 4.1 64.0 средние
2 cofix 64 4.1 14.0 средние
3 кофепорт 40 4.2 40.5 низкие
4 cofefest 31 4.0 0.0 средние
5 кофемания 21 4.5 80.0 высокие
6 cinnabon 19 3.9 45.0 низкие
7 правда кофе 12 4.3 0.0 низкие
8 krispy kreme 9 4.2 98.0 средние
9 даблби 8 4.3 57.0 средние
In [94]:
# Выведем частоту ценовых категорий по всем кофейням
df_coff['price'].value_counts()
Out[94]:
средние          368
низкие            66
высокие           17
выше среднего     14
Name: price, dtype: int64
In [95]:
# У "wild bean cafe" не указана ценовая категория, не будем ее выводить на график
top_10_coffe = top_10_coffe.query('name != "wild bean cafe"')

# Построим столбчатый график распределения ценовой категории кофейн топ-10 
fig = px.bar(
    top_10_coffe,
    x='count',
    y='name',
    text='count',
    template='plotly_white', 
    color='price'                   
)
# оформляем график
fig.update_layout(
    title='Распределения кофейн топ-10 по ценовым категориям',
    width=700, 
    height=400,
    xaxis_title='Количество кофейн',
    yaxis_title='Название кофейни'
)
#fig.update_xaxes(range=[4.1, 4.4])
fig.show("png");
No description has been provided for this image
  • Сетевых кофейн 51% от общего числа кофен, что говорит о популярности сетевых кофейн.
  • Среди сетевых кофейн в топ-10 по количеству попали: Шоколадница; Оne price coffee; Сofix; Кофепорт; Сofefest; Кофемания; Сinnabon; Правда кофе; Кrispy kreme; Сoffeekaldi's.
  • Большее количество кофейн у Шоколадницы, при этом цены средние.
  • Самые высокие рейтинги у Кофемании, но и цены у этих кофейн тоже высокие.
  • Среди кофейн основная ценовая категория - средняя.
  • Средний показатель посадки у кофейн встречается разный, бывает и ноль (которые работают по системе кофе с собой) так и 98 единиц.

Вернуться в начало проекта

Проверим корреляцию между некоторыми параметрами кофейн¶

In [96]:
# Построим график распределения рейтинга и количества посадочных мест
#plt.figure(figsize=(18, 9))

sns.jointplot(x = 'seats', y = 'rating', data = df_coff, kind = 'reg')

plt.title('Зависимоть между рейтингом и количеством посадочных мест')
plt.xlabel('Количество посадочных мест')
plt.ylabel('Рейтинг')
plt.show();

print('Коэффициент корреляции:', round(df_coff['rating'].corr(df_coff['seats']), 2))
No description has been provided for this image
Коэффициент корреляции: -0.02

Коэффициент корреляции -0.02 ничтожно мал, зависимости как таковой нет.

Проверим есть ли зависимость между рейтингом и стоимостью чашки капучино.

In [97]:
# Построим график распределения рейтинга и стоимости чашки капучино
#plt.figure(figsize=(18, 9))

sns.jointplot(x = 'middle_coffee_cup', y = 'rating', data = price_cof_cup, kind = 'reg')

plt.title('Зависимоть между рейтингом и стоимостью чашки капучино')
plt.xlabel('Стоимость чашки капучино')
plt.ylabel('Рейтинг')
plt.show();

print('Коэффициент корреляции:', round(
    price_cof_cup['rating'].corr(price_cof_cup['middle_coffee_cup']), 
    2
))
No description has been provided for this image
Коэффициент корреляции: 0.14

Коэффициент корреляции очень маленький 0.16, говорит о том что зависимости между рейтингом и стоимостью чашки капучино нет.

Посмотрим на коэффициент корреляции между количеством посадочных и стоимостью чашки капучино.

In [98]:
# Построим график распределения количества посадочных мест и стоимости чашки капучино
#plt.figure(figsize=(18, 9))

sns.jointplot(x = 'middle_coffee_cup', y = 'seats', data = price_cof_cup, kind = 'reg')

plt.title('Зависимоть между количеством посадочных мест и стоимостью чашки капучино')
plt.xlabel('Стоимость чашки капучино')
plt.ylabel('Количество посадочных мест')
plt.show();

print('Коэффициент корреляции:', round(
    price_cof_cup['seats'].corr(price_cof_cup['middle_coffee_cup']), 
    2
))
No description has been provided for this image
Коэффициент корреляции: 0.01

Все поверенные корреляции оказались маленькими и незначительными, это говорит о том что такие показатели как рейтинг и количество посадочных мест не влияют на цены в кофейнях Москвы. Вероятнее всего на ценообразование в кофейнях влияют другие параметры, которые мы не можем проверить в рамках данного проекта.

Вернуться в начало проекта

ТОП-15 улиц с самой высокой средней ценой чашки кофе и с самыми высокими средними рейтингами¶

In [99]:
# в сводной таблице посчитаем количество заведений по каждой улице
street_top = df.pivot_table(
    index='street', 
    values=['middle_coffee_cup', 'rating'],
    aggfunc={'middle_coffee_cup' : 'mean', 'rating' : 'mean'}
).sort_values(by=['middle_coffee_cup', 'rating'], ascending=False).head(15)

# сформируем список с наименованием улиц
street_top_cup = street_top.index
street_top = street_top.reset_index()

print('Топ-15 улиц по самому высокому среднему чеку и рейтингу:')
display(street_top)
Топ-15 улиц по самому высокому среднему чеку и рейтингу:
street middle_coffee_cup rating
0 большая никитская улица 328.0 4.564286
1 3-я фрунзенская улица 320.0 4.300000
2 богословский переулок 300.0 4.600000
3 большая сухаревская площадь 300.0 4.375000
4 площадь победы 291.0 4.383333
5 улица гарибальди 291.0 4.331250
6 3-й крутицкий переулок 291.0 4.260000
7 милютинский переулок 290.0 4.450000
8 енисейская улица 287.0 4.326667
9 стремянный переулок 285.0 4.477778
10 мясницкая улица 283.0 4.424000
11 улица 1812 года 275.0 4.566667
12 спиридоньевский переулок 275.0 4.550000
13 улица охотный ряд 275.0 4.400000
14 улица серпуховский вал 275.0 4.344444
In [100]:
# Построим график распределения средней стоимости капучино по топ-15 улицам Москвы
fig = px.bar(
    street_top.sort_values(
        by=['middle_coffee_cup', 'rating'], 
        ascending=False
    ),
    x='middle_coffee_cup',
    y= street_top_cup,
    text='middle_coffee_cup',
    color='rating',
)
# оформляем график
fig.update_layout(
    title='Распределение средней стоимости капучино по топ-15 улицам Москвы',
    width=700, 
    height=450,
    xaxis_title='Средняя цена чашки кофе',
    yaxis_title='Название улицы',
    yaxis={'categoryorder':'total ascending'}
)
fig.update_xaxes(range=[260, 340])
fig.show("png");
No description has been provided for this image

В топ-15 улиц с самой высокой средней стоимостью чашки кофе вошли следующие улицы Москвы:

  1. Большая Никитская улица;
  2. 3-я Фрунзенская улица;
  3. Богословский переулок;
  4. Большая Сухаревская площадь;
  5. площадь Победы;
  6. улица Гарибальди;
  7. 3-й Крутицкий переулок;
  8. Милютинский переулок;
  9. Енисейская улица;
  10. Стремянный переулок;
  11. Мясницкая улица;
  12. улица 1812 года;
  13. Спиридоньевский переулок;
  14. улица Охотный ряд;
  15. улица Серпуховский вал.

Большая часть улиц из этого списка находится в центральном районе Москвы в пределах Садового кольца.

Вернуться в начало проекта

Вывод¶

В ходе детализированного исследования кофейн Москвы было выявлено:

  • Всего представлено 1 413 кофейн.
  • Больше всего кофейн в Центральном районе Москвы, их количество превышает более чем в два раза над остальными районами. Это говорит о высокой конкуренции в данном районе, его не стоит рассматривать для открытия кофейни.
  • Меньше всего кофейн в Северо-Западном административном округе. Это перспективное направление для развития.
  • Доля круглосуточных кофейн в Москве составляет 4.2% от общего числа кофейн. Это перспективное направление для развития.
  • Большая часть круглосуточных кофейн находятся в Центральном округе, они расположились в доль садового кольца, на ул. Новый Арбат, Большая Никитская и других проходных улицах.
  • В остальных районах круглосуточные кофейни большая редкость, чаще всего они распологаются вдоль больших проспектов.
  • В целом разброс в значениях между средними рейтингами кофейн по районам Москвы не большой между 4.2 и 4.34.

При открытии кафе стоит ориентироваться на среднюю стоимость чашки капучино внутри того округа где планируется открывать кофейню, потому что чашка капучино это как маячок для посетителей кофейни, если будет завышена цена, то это может создать впечатление что в заведении слишком высокие цены и средний посетитель не будет заходить к вам в заведение. Если сделать слишком низкую цену на капучино, то это может быть сигналом о том что в данном заведении подают некачественный кофе, и будет предвзятое отношение. Средняя цена за чашку капучино по Москве колеблется по районам в пределах от 151 руб. до 190 руб., так самые высокие цены на капучино в Западном (190 руб.), Центральном (188 руб.) и Юго-Западном (184 руб.) округах; а самые низкие цены на чашку капучино в Юго-Восточном округе - 151 руб и и Южном административном округе - 158 руб.

  • В Москве большой популярностью пользуются сетевые кофейни их доля составляет 51% от общего числа кофен.
  • Среди сетевых кофейн в топ-10 по количеству попали: Шоколадница; Оne price coffee; Сofix; Кофепорт; Сofefest; Кофемания; Сinnabon; Правда кофе; Кrispy kreme; Сoffeekaldi's.
  • Большее количество кофейн у Шоколадницы, при этом цены у этой сети средние.
  • Самые высокие рейтинги у Кофемании, но и цены у этих кофейн тоже высокие.
  • Среди кофейн основная ценовая категория - средняя.
  • Средний показатель посадки у кофейн встречается разный, бывает и ноль (которые работают по системе кофе с собой) так и 98 единиц.
  • Взаимосвязи между типами цен, ценой чашки кофе и показателями рейтинга, количества посадочных мест не выявлено.
  • В топ-15 улиц с самой высокой средней стоимостью чашки кофе вошли следующие улицы Москвы:
  1. Большая Никитская улица;
  2. 3-я Фрунзенская улица;
  3. Богословский переулок;
  4. Большая Сухаревская площадь;
  5. площадь Победы;
  6. улица Гарибальди;
  7. 3-й Крутицкий переулок;
  8. Милютинский переулок;
  9. Енисейская улица;
  10. Стремянный переулок;
  11. Мясницкая улица;
  12. улица 1812 года;
  13. Спиридоньевский переулок;
  14. улица Охотный ряд;
  15. улица Серпуховский вал.

Большая часть улиц из этого списка находится в центральном районе Москвы в пределах Садового кольца.

Рекомендации:

  1. Если не боятся конкуренции, то можно открывать кофейню в любом из районов, но если есть цель открыть доступную кофейню, то лучше не рассматривать такие районы как Центральный и Западный, вероятнее всего цены на аренду помещений в этих районах завышены и сказываются на ценообразовании. Стоит присмотреться к списку топ-15 улиц с самой высокой средней ценой чашки кофе и с самыми высокими средними рейтингами.

  2. Стоит открыть сеть качественных круглосуточных кофейн со средней стоимостью чашки капучино приравненной к средней стоимости чашки капучино в том районе где планируется открытие кофейни.

  3. Лучше всего распологать кофейни в проходимых местах недалеко от метро, бизнес центров, высших учебных заведений или студенческих общежитий.

Вернуться в начало проекта

Итоговый вывод по проекту¶

В ходе работы над проектом были:

  1. Загружены и предобработанны данные о заведениях общественного питания Москвы, составленные на основе сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года.

  2. Проведен исследовательский анализ данных, на данном этапе выявлено что:

  • Структура рынка: преобладают кафе (28,3%) и рестораны (24,3%), третье место занимают кофейни (16,8%). Реже встречаются пекарни и столовые.

  • Вместимость: Большинство заведений имеют от 0 до 100 мест. Кафе, пекарни, столовые, фаст-фуды и кофейни обычно имеют 4–8 средних мест и 46–55 средних мест.

  • Сетевые и несетевые заведения: 62% не являются участниками сети. Кофейни, пиццерии и пекарни имеют больше сетевых заведений. Сетевые заведения обычно располагаются вблизи мест с интенсивным движением и высокой проходимостью (метро, торговые центры, бизнес-центры, парки).

  • Географическое расположение: В Центральном районе самая высокая концентрация заведений, особенно кофеен, ресторанов и кафе. В других округах распределение категорий заведений и их количество более равномерное между районами.

  • Рейтинги: Средний рейтинг варьируется от 4,0 до 4,4 по категориям, и не имеет большого разброса в пунктах не по территориальному принципу, не по ценовому не по категориальному. Бары/пабы лидируют с показателем 4,39.

  • Ценовая категория: Самый высокий средний чек — в Центральном и Западном административных округах (около 1000 рублей), в остальных округах — меньший.

  • Круглосуточные заведения: они составляют 8,7% всех заведений, причем наиболее распространенными в этой категории являются кафе, фаст-фуды и рестораны.

  • Заведения с низким рейтингом: чаще всего среди заведений с низкими рейтингами встречаются кафе и фастфуд, реже всего с низким рейтингом можно встретить пиццерию. У заведений с низким рейтингом ниже и средний чек, что логично, ведь снижая цены зачастую снижается качество продуктов и сервиса, зато растет доступность.

  1. Проведено детализированное исследование открытие кофейни:
  • Всего представлено 1 413 кофейн города Москвы.
  • Самый высокий показатель количества кофейн в Центральном округе.
  • В Северо-Западном округе меньше всего, что указывает на потенциал для новых открытий.
  • Круглосуточные кофейни — перспективное направление, преимущественно расположенное в Центральном округе.
  • Сетевые кофейни составляют 51% рынка.
  • К ведущим сетям относятся «Шоколадница» , «Кофемания » и другие, с разными ценами и рейтингами. Чаще для кофейн характерен средний диапазон цен.
  • Не выявлено корреляции между ценой, рейтингом и количеством посадочных мест.
  • Цена капучино как показатель качества. Средняя цена варьируется в зависимости от района, самая высокая в Западном, Центральном и Юго-Западном округах.
  • Составлен ТОП-15 улиц с самой высокой средней ценой чашки кофе и с самыми высокими средними рейтингами, в который вошли следующие улицы Москвы:
  1. Большая Никитская улица;
  2. 3-я Фрунзенская улица;
  3. Богословский переулок;
  4. Большая Сухаревская площадь;
  5. площадь Победы;
  6. улица Гарибальди;
  7. 3-й Крутицкий переулок;
  8. Милютинский переулок;
  9. Енисейская улица;
  10. Стремянный переулок;
  11. Мясницкая улица;
  12. улица 1812 года;
  13. Спиридоньевский переулок;
  14. улица Охотный ряд;
  15. улица Серпуховский вал.

На основе комплексного аналитического отчета о городских кофейн Москвы рекомендации для инвестора, желающего открыть новую сеть городских кофеен:

  1. Выбор местоположения: Рассмотрите Северо-Западный административный округ из-за более низкой конкуренции и неиспользованного рыночного потенциала.

  2. 24-часовая модель: изучите возможность открытия круглосуточного кафе, особенно за пределами Центрального района.

  3. Сетевая или независимая кофейня: взвесьте преимущества присоединения к сети по сравнению с открытием независимой кофейни, учитывая высокую распространенность сетей.

  4. Стратегия ценообразования: привести цену чашки капучино в соответствие со средней ценой по району, чтобы сбалансировать восприятие качества и доступности.

  5. Места и услуги: Рассмотрите сочетание количества мест и, возможно, модель кофе с собой, исходя из местных предпочтений и существующей конкуренции. Стоит пресмотреться к улицам вошедших в топ-15 по самой высокой средней стоимостью чашки кофе и средниму рейтингу. Большая часть улиц из этого списка находится в центральном районе Москвы в пределах Садового кольца.

Сосредоточив внимание на этих областях, инвестор сможет стратегически позиционировать новую сеть кофеен для достижения успеха на конкурентном рынке городских кофеен Москвы.

ПРЕЗЕНТАЦИЯ: https://disk.yandex.ru/d/Kifj9kFiAc9pYQ

Вернуться в начало проекта